diff --git a/AUTHORS b/AUTHORS index 3d447da56a..3c4802c4ae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -66,6 +66,7 @@ Zhuo Qingliang # BEGIN organizations section. Ada Logics Ltd. <*@adalogics.com> +Advanced Micro Devices, Inc. <*@amd.com> Collabora Ltd. <*@collabora.com> DocsCorp Pty Ltd. <*@docscorp.com> Dropbox <*@dropbox.com> diff --git a/DEPS b/DEPS index 99c5b5d917..2baa91ef58 100644 --- a/DEPS +++ b/DEPS @@ -74,10 +74,6 @@ vars = { # RBE project to download rewrapper config files for. Only needed if # different from the project used in 'rbe_instance' 'rewrapper_cfg_project': Str(''), - # reclient CIPD package - 'reclient_package': 'infra/rbe/client/', - # reclient CIPD package version - 'reclient_version': 're_client_version:0.185.0.db415f21-gomaip', 'chromium_git': 'https://chromium.googlesource.com', 'pdfium_git': 'https://pdfium.googlesource.com', @@ -86,7 +82,7 @@ vars = { # 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': 'a00d74bb55b38f213a3c6110b4bcab5bb565bd56', + 'abseil_revision': '56722f9eb6f40fb1a140caffd36e8f1f61b57aaf', # 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. @@ -98,11 +94,11 @@ vars = { # 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': 'ebdaeefa62db7c40f27cacb0eb034db3a5cdb849', + 'build_revision': '0c62137b8623a3fab2c6aa5d7c94f719b3aa31f6', # 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': '64e87f9fbdaf6f53f6cff8f15e4b06f17cead514', + 'buildtools_revision': 'd0d4c192e3c718016b3a0c5d44ccec632bccfe96', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. @@ -114,7 +110,7 @@ vars = { # 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': '52b22f34fa143721c794752f01fbd9f95e7692f0', + 'clang_revision': '7ea1bdeeaf498a513c77b66667e56f6f41f9f0b3', # 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. @@ -188,7 +184,7 @@ vars = { # and whatever else without interference from each other. # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. - 'libcxx_revision': '97b436da4c33663581d394f4ee0a5977fc38c2f4', + 'libcxx_revision': '9ba7713d3a0f1e179904c984bb626748b15e57d6', # 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. @@ -204,7 +200,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libyuv # and whatever else without interference from each other. - 'libyuv_revision': 'eb8eda9c7973704d1103a535059224df7a6063b5', + 'libyuv_revision': '1e2e3a35993609da1d89ac170b907719d555f4b3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. @@ -224,7 +220,7 @@ vars = { # 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': 'dc6144ac93b23232ea8d2b931f661a917ad7d4fd', + 'partition_allocator_revision': '2b864a17731ee7ae5a994ea308178da1e444ba29', # 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. @@ -236,7 +232,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling rust # and whatever else without interference from each other. - 'rust_revision': 'b8c38a533c84f01fe194fed455f1d53132b0cf36', + 'rust_revision': '79a8603b966b612119e3498bcf7e535dfa096c1c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling simdutf # and whatever else without interference from each other. @@ -248,7 +244,7 @@ vars = { # 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': '5b1a7f7c06448e5d5f38f3c9360b18cae315576e', + 'skia_revision': '0abfcf118530e0e46e7e466f6d028041af61581a', # 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. @@ -256,7 +252,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling testing_rust # and whatever else without interference from each other. - 'testing_rust_revision': '9c16e2f4c17e384708c68ecc8596c80df5689b48', + 'testing_rust_revision': '9022dfb9a7ba7ad28fe7798ed997ba57cd52335b', # 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. @@ -264,7 +260,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling tools_rust # and whatever else without interference from each other. - 'tools_rust_revision': 'e61a63fae23826e5bf5650a1bd7da255ba950577', + 'tools_rust_revision': 'b7b03f53fe7d9e6b781bd1837d92275279ce5794', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling tools_win_revision # and whatever else without interference from each other. @@ -326,16 +322,6 @@ deps = { 'condition': 'host_os == "mac"', }, - 'buildtools/reclient': { - 'packages': [ - { - 'package': Var('reclient_package') + '${{platform}}', - 'version': Var('reclient_version'), - } - ], - 'dep_type': 'cipd', - }, - 'buildtools/win': { 'packages': [ { @@ -503,171 +489,171 @@ deps = { 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/clang-android-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '9551f7013b0210e4d291bfe09191682796240d93002b4317673c7fc280045e62', - 'size_bytes': 6207356, - 'generation': 1790797384572756, + 'object_name': 'Linux_x64/clang-android-runtime-library-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'dd03492cad72f223d7743c2f4af39c405f5bac7f7f342de09697a45b7108ce4e', + 'size_bytes': 6188284, + 'generation': 1792086693754706, 'condition': 'checkout_android and non_git_source', }, { - 'object_name': 'Linux_x64/clang-linux-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '4e5a4831e4eec7408c0809d2033032d157fe82dc16b5096d32b6536ed4b8c43b', - 'size_bytes': 3102700, - 'generation': 1790797389651907, + 'object_name': 'Linux_x64/clang-linux-runtime-library-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '38338f434311fcd12a070757c23830d6076529f4dcbefebc9d094804dc4eca63', + 'size_bytes': 3145380, + 'generation': 1792086698521278, 'condition': 'checkout_linux and not host_os == "linux"', }, { - 'object_name': 'Linux_x64/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '878495372b935b667f84203f9f62529b6191ff1ac6ab40b9cdf482c84bd1ecab', - 'size_bytes': 57260672, - 'generation': 1790797381909597, + 'object_name': 'Linux_x64/clang-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '0b76afc1a29b0583f7723e966b9b65a22b15d8fdcd38e7c567ae32343c68944d', + 'size_bytes': 58394648, + 'generation': 1792086691304470, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Linux_x64/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '3815203f9987a08b1bf67555125d95a67c1136de074e6e7dbe11957d70cf784d', - 'size_bytes': 14944572, - 'generation': 1790797381856065, + 'object_name': 'Linux_x64/clang-tidy-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'cf2a7610086b45fb878f606709babd3cbd8e26ea97a087ea47929e4ee871b562', + 'size_bytes': 15213064, + 'generation': 1792086691338093, 'condition': 'host_os == "linux" and checkout_clang_tidy and non_git_source', }, { - 'object_name': 'Linux_x64/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '84ee10cf3a4b4cccf9878c464f33eee756751920187d26dfc46481f195b0c177', - 'size_bytes': 15081268, - 'generation': 1790797381894014, + 'object_name': 'Linux_x64/clangd-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '5ef64ed6fdeab8dfd91f11db07c601ce5107fc9e61e9d7a18dcee97af42f03c6', + 'size_bytes': 15595776, + 'generation': 1792086691391942, 'condition': 'host_os == "linux" and checkout_clangd and non_git_source', }, { - 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'f9990e7dc5a3630c8a962726fac172eeff5c6012e027eb342e9df8bc702198c8', - 'size_bytes': 2373504, - 'generation': 1790797382242083, + 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '756e08684179166571dd155471e0a9c4525223d30b45d776b3d75a4d53b820d3', + 'size_bytes': 2432768, + 'generation': 1792086691721951, 'condition': 'host_os == "linux" and checkout_clang_coverage_tools and non_git_source', }, { - 'object_name': 'Linux_x64/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'ad3042a17255b065a75fa7f9d80be2eb07df0902bd762cec53b9f2f5c3beba87', - 'size_bytes': 5925392, - 'generation': 1790797381953168, - 'condition': '((checkout_linux or checkout_mac or checkout_android) and host_os == "linux") and non_git_source', + 'object_name': 'Linux_x64/llvmobjdump-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '2282c8a4bdc6e12242989525039a36e7eeb4335b22d39d3b5d624c38083881c5', + 'size_bytes': 6011380, + 'generation': 1792086691439732, + 'condition': '((checkout_linux or checkout_mac or checkout_ios or checkout_android) and host_os == "linux") and non_git_source', }, { - 'object_name': 'Mac/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'eb03cb8b944dde55677427322bc5a8c6fa0cad4ceacba4653a42c6b1290fea2f', - 'size_bytes': 53873488, - 'generation': 1790797391417548, + 'object_name': 'Mac/clang-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'ae29ec360eda6e80e96f8638d368ecdeea93d19c18c527c525dc0932bd5243a4', + 'size_bytes': 54834700, + 'generation': 1792086700293656, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '31569c5a0a8e133ea5033d3a462273ce65100e0402307319af644ee4fb78d77c', - 'size_bytes': 978876, - 'generation': 1790797399057345, - 'condition': 'checkout_mac and not host_os == "mac"', + 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'c18444eef64cf27a8913ce73ffa8fd92e6f41306a02bbc82d805aa0c521863e0', + 'size_bytes': 997164, + 'generation': 1792086707123823, + 'condition': '(checkout_mac or checkout_ios) and not host_os == "mac" and non_git_source', }, { - 'object_name': 'Mac/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '5dc210d6e7a1f2d8decba74a475bef9457db132a7ae1f4cf0fc4b91fd008e146', - 'size_bytes': 14224492, - 'generation': 1790797391469624, + 'object_name': 'Mac/clang-tidy-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '3997f34e72dc28eb30af1b8af903ceb6b0f516db7821996297bf296793addc72', + 'size_bytes': 14437076, + 'generation': 1792086700400707, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy', }, { - 'object_name': 'Mac/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'f988b7dc66993f8612ac036b62828bcb29315ec9e6927331bf6a32bf986a5df7', - 'size_bytes': 15131696, - 'generation': 1790797391569653, + 'object_name': 'Mac/clangd-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '0fc9c351ad3bc6c357cc53a57dad6f8ebf45ba9e38150012cb457812f2a1314f', + 'size_bytes': 15359244, + 'generation': 1792086700318835, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd', }, { - 'object_name': 'Mac/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'acb0db493e23a145f733a871ac868468932be7fb4988fe2b7b9b568e70c45682', - 'size_bytes': 2260736, - 'generation': 1790797391738507, + 'object_name': 'Mac/llvm-code-coverage-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'b8b2f099cb029ff36cce33c9983984f547d7da0d9c34c1b427bb1080a31b8047', + 'size_bytes': 2307268, + 'generation': 1792086700464636, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '85b912bf5182b5c7092ce92cdc58a35b5e3d04fa9a49514aa51b28bec4c277fc', - 'size_bytes': 5524828, - 'generation': 1790797391756333, + 'object_name': 'Mac/llvmobjdump-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'eae7800773f3955fd1dcc334d8779b9a232ca6ebd3a79a55be028bd8797b1197', + 'size_bytes': 5586688, + 'generation': 1792086700364844, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'bbf4ee5fc42bf134aee682d6afaaaddcc64415d20b080e349a45abf6df927070', - 'size_bytes': 44508976, - 'generation': 1790797400949995, + 'object_name': 'Mac_arm64/clang-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '0a8d887534b3153c7f6860265dc7e075d420174521c60ccd52c2c243949ef468', + 'size_bytes': 44917408, + 'generation': 1792086708845653, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Mac_arm64/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '35b3e2e5eee220259f7908eaf208507aba78f7cfb8dd990c9854558f056ad890', - 'size_bytes': 12220676, - 'generation': 1790797401142057, + 'object_name': 'Mac_arm64/clang-tidy-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '1aee3a0078bbe3b840125f521d3e6d985de61531ee6686a0bef80abc6e6d6a78', + 'size_bytes': 12291060, + 'generation': 1792086708806950, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy', }, { - 'object_name': 'Mac_arm64/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '5dff03b1fdf59ffe374d6c55deb07c65791825737487a7ec2b39d80539144c6b', - 'size_bytes': 12229100, - 'generation': 1790797401064932, + 'object_name': 'Mac_arm64/clangd-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '799e034e1fdff7eb7d899329ab20bbeb96642234844c9e070797059bd6ce5ee9', + 'size_bytes': 12292508, + 'generation': 1792086709015693, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd', }, { - 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '1d92f7504529998f971fffbb2dd6214273ed3e6bcfe66e5ca9c1227856df5b4b', - 'size_bytes': 1861564, - 'generation': 1790797401395439, + 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'a77bacfb3e6c08af7d51981d88fb4aa07e4fb797f37937071dd8657b7c2dea8e', + 'size_bytes': 1897140, + 'generation': 1792086709049271, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '0db8f38d5928777104b9d04fc4bcf9c19c17ea252a0745ca038565e3094be9d4', - 'size_bytes': 5235388, - 'generation': 1790797401139458, + 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '8d15119ffd7e9d52e86676bfdcfd8159506f0ba6c4b292ae4112cbf15e629d7d', + 'size_bytes': 5269524, + 'generation': 1792086709009144, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '86ac9f61c4350f17d7571e402d5fa5f2367015d72ea8efbc4e1e52e843c74fb0', - 'size_bytes': 51822620, - 'generation': 1790797410692058, + 'object_name': 'Win/clang-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '08abce6e1d01c2fff9b25a09e36d9976296f27842196f96411b8220364481818', + 'size_bytes': 52783904, + 'generation': 1792086718487613, 'condition': 'host_os == "win"', }, { - 'object_name': 'Win/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '7d428d76bf66dd603860270ba0a41e88bbbb66c32770aae541c3a6777a9eb57f', - 'size_bytes': 15133896, - 'generation': 1790797410629185, + 'object_name': 'Win/clang-tidy-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'ee25e107e6da91254780b0e9b6fb34198dc03636330c5564a7786e6450916d45', + 'size_bytes': 15436080, + 'generation': 1792086718412245, 'condition': 'host_os == "win" and checkout_clang_tidy', }, { - 'object_name': 'Win/clang-win-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '0ea5939bf24f293c63621b81d90a88284b6d6563a51be1304b73d2cf92537ffc', - 'size_bytes': 2652404, - 'generation': 1790797418368249, + 'object_name': 'Win/clang-win-runtime-library-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': 'a156f6fa1f149e45faa683f2b2d1cc010df1e29912b3cb0a0dd3e3d9186c513b', + 'size_bytes': 2707764, + 'generation': 1792086725161419, 'condition': 'checkout_win and not host_os == "win"', }, { - 'object_name': 'Win/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '70955f11ea7a22b25807d00b97351c3853d8a9b1e8d1d08c165bc015320858c1', - 'size_bytes': 15434728, - 'generation': 1790797563126146, + 'object_name': 'Win/clangd-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '0db39ead1a1f49137570fd5e99ab7520262c2e6444900b551862ae2cccdf6bb8', + 'size_bytes': 15910376, + 'generation': 1792086718402867, 'condition': 'host_os == "win" and checkout_clangd', }, { - 'object_name': 'Win/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': 'b07c06d983817b4f98878bcc264825835efbd9e5548a18a7d6dfb7623b9c4024', - 'size_bytes': 2527180, - 'generation': 1790797410912461, + 'object_name': 'Win/llvm-code-coverage-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '3cfd8304148bd6bdd86a99f4e5f01ea2b4fbcbf4f35e29a2ca3cb235cb88c393', + 'size_bytes': 2604364, + 'generation': 1792086718581883, 'condition': 'host_os == "win" and checkout_clang_coverage_tools', }, { - 'object_name': 'Win/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', - 'sha256sum': '7b34bdb18311a40a234c19860b2c289f6cd7ce16f12d3a449dcf6db6dff043eb', - 'size_bytes': 6010064, - 'generation': 1790797410754258, + 'object_name': 'Win/llvmobjdump-llvmorg-24-init-14453-ge063be79-1.tar.xz', + 'sha256sum': '3b58242027a9e86114d05444e9e70a9ed497a6a9e9c14d1b86c355e140503a53', + 'size_bytes': 6123208, + 'generation': 1792086718463583, 'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"', }, ] @@ -682,31 +668,31 @@ deps = { 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/rust-libclang-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': 'b251621641190cdab7d23c4a47257e4e1f1f335c34c9676118940fa562bb66ff', - 'size_bytes': 22776236, - 'generation': 1790798150313742, + 'object_name': 'Linux_x64/rust-libclang-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': '693fd176a27508efb6cbc06b7db9c64abfc19497664dbaf4a9f4d489b8b4239a', + 'size_bytes': 23021680, + 'generation': 1793195408791425, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Mac/rust-libclang-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': '4f7e9d166ad2ced03b9d5c66f2a919fefce5127d03dbe93661a0d012c1dda9ef', - 'size_bytes': 22924716, - 'generation': 1790798154061688, + 'object_name': 'Mac/rust-libclang-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': 'b08bf161b5acaba2fe39d9a8089f8134417bfaef33531b6e4e84365e7c828bfe', + 'size_bytes': 23159048, + 'generation': 1793195412269519, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/rust-libclang-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': '444cfa9bd9d3cda991943c9743f1992004b1f50d6641372b5ff9103d9a4db478', - 'size_bytes': 20588924, - 'generation': 1790798157457698, + 'object_name': 'Mac_arm64/rust-libclang-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': 'aa01ed55d80721dc53acf6fb604113037ba5142f3668ce7f7a02427b787cfea5', + 'size_bytes': 20842496, + 'generation': 1793195415799242, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/rust-libclang-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': 'e8bb31ac1bded5c1fce08567a8a1a8f304213cceeee7384c2cba844dcaddfd20', - 'size_bytes': 21527244, - 'generation': 1790798161045212, + 'object_name': 'Win/rust-libclang-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': '38438c49085b1770020713fc981bf63a6e7d6398d8c1d7f094ec509347d384e8', + 'size_bytes': 21764276, + 'generation': 1793195419458813, 'condition': 'host_os == "win"', }, ], @@ -745,31 +731,31 @@ deps = { 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': '1f5b945345630c2d5c91a53fd506af476d617b79f251c7ccd2bb707a1f0faa92', - 'size_bytes': 275879080, - 'generation': 1790798148502695, + 'object_name': 'Linux_x64/rust-toolchain-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': '4b4f64d5e82e845099b6ff63c61df311f8202183f1996ca247e20db6d9cda29c', + 'size_bytes': 287835700, + 'generation': 1793195406905214, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Mac/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': 'd2ca8d43ebd3e5ce956cfa9e1e7a292cf3faedd0113550a047d08a5cff5d10bf', - 'size_bytes': 263208960, - 'generation': 1790798152115147, + 'object_name': 'Mac/rust-toolchain-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': '7bd89fb1973d32d0231a1ffcd48851d629986763695c43ba6e8d8182361c538f', + 'size_bytes': 274877668, + 'generation': 1793195410604331, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': '68b2b16c3bf32a141fa6ba8e3c51887d6c8be44659b7b8f188d9af16543bf869', - 'size_bytes': 247890912, - 'generation': 1790798155770959, + 'object_name': 'Mac_arm64/rust-toolchain-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': '7d1680ebf1d9ea133cd34d5f229c8491fbdbf9fe0da24171b686c8ecb8a4cad8', + 'size_bytes': 258546192, + 'generation': 1793195414074050, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', - 'sha256sum': '630ccbd409fcef327cba0044bf72012f4023fab890a8db26c9731a03d5bdcb8e', - 'size_bytes': 416254844, - 'generation': 1790798159238637, + 'object_name': 'Win/rust-toolchain-8a9dd7abdf013ec10cfeaaa3685feba18ef1c90c-6-llvmorg-24-init-14453-ge063be79.tar.xz', + 'sha256sum': '9e519f453f7fea971f531b7e4c5a8e686081b9647d7660deb0641efac306840f', + 'size_bytes': 426696244, + 'generation': 1793195417684260, 'condition': 'host_os == "win"', }, ], diff --git a/README.md b/README.md index bb3866a506..daa08494fa 100644 --- a/README.md +++ b/README.md @@ -215,10 +215,10 @@ Chromium provides code coverage reports for PDFium `third_party/pdfium` in Chromium's source code. This includes code coverage from PDFium's fuzzers. -## Waterfall +## Infrastructure -The current health of the source tree can be found -[here](https://ci.chromium.org/p/pdfium/g/main/console). +See [docs/infra.md](/docs/infra.md) for details about PDFium's continuous +integration and build infrastructure. ## Community diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni index e89d55372a..edad9a0cc2 100644 --- a/build_overrides/pdfium.gni +++ b/build_overrides/pdfium.gni @@ -14,8 +14,8 @@ pdf_bundle_freetype_override = true pdf_enable_freetype_override = true # Build PDFium with experimental Brotli compression support. -# Default: Brotli support is disabled until PDF 2.0 spec is confirmed. -pdf_enable_brotli_override = false +# Default: Brotli support is compiled in by default. +pdf_enable_brotli_override = true # Build PDFium either with or without v8 support. # Default: With V8 support. @@ -36,11 +36,6 @@ pdf_use_partition_alloc_override = is_clang # The default is to use AGG when `pdf_use_skia_override` is false. pdf_use_skia_override = false -# When building PDFium with Skia enabled, optionally define a custom font -# manager target that is used to override the pre-chosen font manager. -# Default: No override. Use the pre-chosen font manager. -pdf_skia_font_manager_target_override = "" - # Build PDFium with experimental Fontations library support. # If enabled, coexists in build with FreeType library and the default font # library is selectable at runtime. diff --git a/core/fpdfapi/edit/cpdf_creator.cpp b/core/fpdfapi/edit/cpdf_creator.cpp index 02ce2f1cf3..e10c219dbc 100644 --- a/core/fpdfapi/edit/cpdf_creator.cpp +++ b/core/fpdfapi/edit/cpdf_creator.cpp @@ -506,18 +506,19 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage1() { } if (stage_ == Stage::kWriteHeader10) { if (!is_incremental_) { - if (!archive_->WriteString("%PDF-1.")) { + if (!archive_->WriteString("%PDF-")) { return Stage::kInvalid; } - int32_t version = 7; + int32_t version = 17; if (file_version_) { version = file_version_; } else if (parser_) { version = parser_->GetFileVersion(); } - if (!archive_->WriteDWord(version % 10) || + if (!archive_->WriteDWord(version / 10) || !archive_->WriteString(".") || + !archive_->WriteDWord(version % 10) || !archive_->WriteString("\r\n%\xA1\xB3\xC5\xD7\r\n")) { return Stage::kInvalid; } diff --git a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp index 8eea25fa2d..656bf9020f 100644 --- a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp +++ b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp @@ -90,3 +90,32 @@ TEST_F(CPDFCreatorEmbedderTest, SaveLinearizedInfo) { EXPECT_THAT(saved_doc, ::testing::HasSubstr("/Info")); } + +TEST_F(CPDFCreatorEmbedderTest, SingleSubsectionXrefWithFreeObjectLinkedList) { + ASSERT_TRUE(OpenDocument("xref_free_objects.pdf")); + ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + + // EmbedPDF(complete-xref): one subsection without gaps (upstream's TODO, + // crbug.com/42271313); objects 1, 3, 5 and 6 are free entries, linked from + // entry 0 in ascending order back to 0. + static constexpr char kExpectedXrefAndTrailerPrefix[] = + "xref\r\n" + "0 8\r\n" + "0000000001 65535 f\r\n" + "0000000003 00000 f\r\n" + "0000000017 00000 n\r\n" + "0000000005 00000 f\r\n" + "0000000066 00000 n\r\n" + "0000000006 00000 f\r\n" + "0000000000 00000 f\r\n" + "0000000122 00000 n\r\n" + "trailer\r\n"; + + EXPECT_THAT(GetString(), testing::HasSubstr(kExpectedXrefAndTrailerPrefix)); + + // Also make sure the saved PDF still loads. + ScopedSavedDoc saved_document = OpenScopedSavedDocument(); + ASSERT_TRUE(saved_document); + ScopedSavedPage saved_page = LoadScopedSavedPage(0); + EXPECT_TRUE(saved_page); +} diff --git a/core/fpdfapi/parser/cfdf_document.cpp b/core/fpdfapi/parser/cfdf_document.cpp index 60dbf8bd7e..7f9577e272 100644 --- a/core/fpdfapi/parser/cfdf_document.cpp +++ b/core/fpdfapi/parser/cfdf_document.cpp @@ -11,6 +11,7 @@ #include #include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_read_validator.h" #include "core/fpdfapi/parser/cpdf_syntax_parser.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" #include "core/fxcrt/cfx_read_only_span_stream.h" @@ -40,7 +41,9 @@ std::unique_ptr CFDF_Document::ParseMemory( void CFDF_Document::ParseStream(RetainPtr pFile) { file_ = std::move(pFile); - CPDF_SyntaxParser parser(file_); + CPDF_SyntaxParser parser( + pdfium::MakeRetain(file_, nullptr), 0, + GetByteStringPool()); while (true) { CPDF_SyntaxParser::WordResult word_result = parser.GetNextWord(); if (word_result.is_number) { diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp index e1833de43b..7c25ffd2c0 100644 --- a/core/fpdfapi/parser/cpdf_parser.cpp +++ b/core/fpdfapi/parser/cpdf_parser.cpp @@ -206,8 +206,9 @@ bool CPDF_Parser::InitSyntaxParser(RetainPtr validator) { return false; } - syntax_ = std::make_unique(std::move(validator), - header_offset.value()); + syntax_ = std::make_unique( + std::move(validator), header_offset.value(), + objects_holder_->GetByteStringPool()); return ParseFileVersion(); } diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp index ef5dc2cf20..e4a2fbb989 100644 --- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp +++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp @@ -95,9 +95,16 @@ CPDF_SyntaxParser::CPDF_SyntaxParser( CPDF_SyntaxParser::CPDF_SyntaxParser(RetainPtr validator, FX_FILESIZE header_offset) + : CPDF_SyntaxParser(std::move(validator), header_offset, /*pool=*/nullptr) { +} + +CPDF_SyntaxParser::CPDF_SyntaxParser(RetainPtr validator, + FX_FILESIZE header_offset, + WeakPtr pool) : file_access_(std::move(validator)), header_offset_(header_offset), - file_len_(file_access_->GetSize()) { + file_len_(file_access_->GetSize()), + pool_(std::move(pool)) { DCHECK_LE(header_offset_, file_len_); } diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.h b/core/fpdfapi/parser/cpdf_syntax_parser.h index dec7b260d4..5be782706f 100644 --- a/core/fpdfapi/parser/cpdf_syntax_parser.h +++ b/core/fpdfapi/parser/cpdf_syntax_parser.h @@ -44,6 +44,9 @@ class CPDF_SyntaxParser { explicit CPDF_SyntaxParser(RetainPtr file_access); CPDF_SyntaxParser(RetainPtr validator, FX_FILESIZE header_offset); + CPDF_SyntaxParser(RetainPtr validator, + FX_FILESIZE header_offset, + WeakPtr pool); ~CPDF_SyntaxParser(); void SetReadBufferSize(uint32_t read_buffer_size) { diff --git a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp index 01212225e2..184f1f4744 100644 --- a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp +++ b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp @@ -8,7 +8,6 @@ #include "public/cpp/fpdf_scopers.h" #include "public/fpdfview.h" #include "testing/embedder_test.h" -#include "testing/embedder_test_constants.h" #include "testing/gtest/include/gtest/gtest.h" class FPDFRenderPatternEmbedderTest : public EmbedderTest {}; @@ -48,8 +47,7 @@ TEST_F(FPDFRenderPatternEmbedderTest, LoadError547706) { ASSERT_TRUE(OpenDocument("bug_547706.pdf")); ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmap(bitmap.get(), pdfium::kBlankPage612By792Png); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "bug_547706"); } TEST_F(FPDFRenderPatternEmbedderTest, ZeroTilingPatternStep509964646) { diff --git a/core/fxcodec/brotli/brotli_decoder.cpp b/core/fxcodec/brotli/brotli_decoder.cpp index cc96b58cfa..ae36324921 100644 --- a/core/fxcodec/brotli/brotli_decoder.cpp +++ b/core/fxcodec/brotli/brotli_decoder.cpp @@ -11,6 +11,7 @@ #include "core/fxcodec/image_predictors.h" #include "core/fxcrt/check.h" #include "core/fxcrt/data_vector.h" +#include "core/fxcrt/epdf_tls.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/numerics/safe_conversions.h" #include "third_party/brotli/include/brotli/decode.h" @@ -18,7 +19,9 @@ namespace { constexpr size_t kMaxDecodeBytes = 1024 * 1024 * 1024; -bool g_brotli_enabled = false; +// EmbedPDF(thread-confined-globals): per thread, as library start-up runs on +// every thread (rule per-thread-init) and sets it from that thread's config. +EPDF_TLS bool g_brotli_enabled = false; struct BrotliDecoderStateDeleter { void operator()(struct BrotliDecoderStateStruct* ptr) const { diff --git a/core/fxcodec/brotli/brotli_embeddertest.cpp b/core/fxcodec/brotli/brotli_embeddertest.cpp index 473eaf09e1..6eb12a8cab 100644 --- a/core/fxcodec/brotli/brotli_embeddertest.cpp +++ b/core/fxcodec/brotli/brotli_embeddertest.cpp @@ -4,7 +4,6 @@ #include "public/fpdfview.h" #include "testing/embedder_test.h" -#include "testing/embedder_test_constants.h" #include "testing/embedder_test_environment.h" #include "testing/gtest/include/gtest/gtest.h" @@ -50,38 +49,24 @@ class BrotliV5EmbedderTest : public BrotliEnabledEmbedderTest { TEST_F(BrotliEnabledEmbedderTest, ManyRectanglesBrotli) { ASSERT_TRUE(OpenDocument("many_rectangles_brotli.pdf")); - ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - ASSERT_TRUE(bitmap); - - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kManyRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "many_rectangles"); } TEST_F(BrotliEnabledEmbedderTest, SimpleBrotliWithText) { ASSERT_TRUE(OpenDocument("hello_world_brotli.pdf")); - ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - ASSERT_TRUE(bitmap); - - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kHelloWorldPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "hello_world"); } TEST_F(BrotliEnabledEmbedderTest, BrotliRectangles) { ASSERT_TRUE(OpenDocument("rectangles_brotli.pdf")); - ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - ASSERT_TRUE(bitmap); - - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "rectangles"); } TEST_F(BrotliEnabledEmbedderTest, BrotliDecodeParms) { @@ -98,36 +83,23 @@ TEST_F(BrotliEnabledEmbedderTest, BrotliDecodeParms) { TEST_F(BrotliEnabledEmbedderTest, BrotliWithLength1Argument) { ASSERT_TRUE(OpenDocument("hello_world_brotli_with_length1.pdf")); - ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - ASSERT_TRUE(bitmap); - - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kHelloWorldPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "hello_world"); } TEST_F(BrotliDisabledEmbedderTest, BrotliDecodeDisabled) { ASSERT_TRUE(OpenDocument("hello_world_brotli.pdf")); - ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - ASSERT_TRUE(bitmap); - - CompareBitmap(bitmap.get(), "blank_200x200"); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "blank_page_200x200"); } TEST_F(BrotliV5EmbedderTest, WrongBrotliVersion) { ASSERT_TRUE(OpenDocument("hello_world_brotli.pdf")); - ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - ASSERT_TRUE(bitmap); - - CompareBitmap(bitmap.get(), "blank_200x200"); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "blank_page_200x200"); } diff --git a/core/fxcodec/fx_codec.cpp b/core/fxcodec/fx_codec.cpp index 3eef9272a7..ec182cc2f8 100644 --- a/core/fxcodec/fx_codec.cpp +++ b/core/fxcodec/fx_codec.cpp @@ -56,9 +56,8 @@ void ReverseRGB(pdfium::span pDestBuf, count); #if defined(PDF_USE_LIBYUV) - libyuv::RGB24ToRAW(pdfium::as_bytes(src_span).data(), pixels * 3, - pdfium::as_writable_bytes(dst_span).data(), pixels * 3, - pixels, 1); + libyuv::RGB24ToRAW(pdfium::as_bytes(src_span).data(), 0, + pdfium::as_writable_bytes(dst_span).data(), 0, pixels, 1); #else if (dst_span.data() == src_span.data()) { for (auto& pix : dst_span) { diff --git a/core/fxcodec/jpeg/jpegmodule.cpp b/core/fxcodec/jpeg/jpegmodule.cpp index 82239252a7..eff1819f78 100644 --- a/core/fxcodec/jpeg/jpegmodule.cpp +++ b/core/fxcodec/jpeg/jpegmodule.cpp @@ -27,6 +27,7 @@ #include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/numerics/safe_conversions.h" #include "core/fxcrt/retain_ptr.h" +#include "core/fxcrt/zip.h" #include "core/fxge/dib/cfx_dibbase.h" #include "core/fxge/dib/fx_dib.h" #endif // BUILDFLAG(IS_WIN) @@ -130,16 +131,16 @@ bool JpegModule::JpegEncode(const RetainPtr& pSource, compress_common.common.cinfo.in_color_space = JCS_CMYK; } - std::unique_ptr line_buf; - if (nComponents > 1) { - line_buf.reset(FX_Alloc2D(uint8_t, width, nComponents)); - } - if (!jpeg_common_set_defaults(&compress_common.common) || !jpeg_common_start_compress(&compress_common.common, TRUE)) { return false; } + std::unique_ptr line_buf; + if (nComponents > 1) { + line_buf.reset(FX_Alloc2D(uint8_t, width, nComponents)); + } + JSAMPROW row_pointer; JDIMENSION row; while (compress_common.common.cinfo.next_scanline < @@ -147,8 +148,8 @@ bool JpegModule::JpegEncode(const RetainPtr& pSource, pdfium::span src_scan = pSource->GetScanline(compress_common.common.cinfo.next_scanline); if (nComponents > 1) { - uint8_t* dest_scan = line_buf.get(); if (nComponents == 3) { + uint8_t* dest_scan = line_buf.get(); UNSAFE_TODO({ for (uint32_t i = 0; i < width; i++) { ReverseCopy3Bytes(dest_scan, src_scan.data()); @@ -157,12 +158,13 @@ bool JpegModule::JpegEncode(const RetainPtr& pSource, } }); } else { - UNSAFE_TODO({ - for (uint32_t i = 0; i < pitch; i++) { - *dest_scan++ = ~src_scan.front(); - src_scan = src_scan.subspan<1u>(); - } - }); + // SAFETY: Matches FX_Alloc2D() call above. + pdfium::span dest_span = UNSAFE_BUFFERS( + pdfium::span(line_buf.get(), width * nComponents)); + for (auto [in, out] : + fxcrt::Zip(src_scan.first(pitch), dest_span.first(pitch))) { + out = ~in; + } } row_pointer = line_buf.get(); } else { diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn index 6b47d26965..d659936be5 100644 --- a/core/fxcrt/BUILD.gn +++ b/core/fxcrt/BUILD.gn @@ -224,6 +224,8 @@ source_set("test_support") { sources = [ "fx_coordinates_test_support.cpp", "fx_coordinates_test_support.h", + "scoped_locale.cpp", + "scoped_locale.h", "string_test_support.cpp", ] configs += [ "../../:pdfium_strict_config" ] diff --git a/testing/scoped_locale.cc b/core/fxcrt/scoped_locale.cpp similarity index 67% rename from testing/scoped_locale.cc rename to core/fxcrt/scoped_locale.cpp index f3055b66cc..6d54b3a1a2 100644 --- a/testing/scoped_locale.cc +++ b/core/fxcrt/scoped_locale.cpp @@ -2,21 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "testing/scoped_locale.h" +#include "core/fxcrt/scoped_locale.h" -#include +#include "build/build_config.h" +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include #include "core/fxcrt/check_op.h" +#endif namespace pdfium { ScopedLocale::ScopedLocale(const std::string& locale) { +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) prev_locale_ = setlocale(LC_ALL, nullptr); CHECK(setlocale(LC_ALL, locale.c_str()) != nullptr); +#endif } ScopedLocale::~ScopedLocale() { +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) CHECK_EQ(prev_locale_, setlocale(LC_ALL, prev_locale_.c_str())); +#endif } } // namespace pdfium diff --git a/testing/scoped_locale.h b/core/fxcrt/scoped_locale.h similarity index 66% rename from testing/scoped_locale.h rename to core/fxcrt/scoped_locale.h index dd0b977582..27138637d0 100644 --- a/testing/scoped_locale.h +++ b/core/fxcrt/scoped_locale.h @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef TESTING_SCOPED_LOCALE_H_ -#define TESTING_SCOPED_LOCALE_H_ +#ifndef CORE_FXCRT_SCOPED_LOCALE_H_ +#define CORE_FXCRT_SCOPED_LOCALE_H_ #include namespace pdfium { -// Sets the given |locale| on construction, and restores the previous locale -// on destruction. +// On certain platforms, sets the given `locale` on construction, and restores +// the previous locale on destruction. class ScopedLocale { public: explicit ScopedLocale(const std::string& locale); @@ -25,4 +25,4 @@ class ScopedLocale { } // namespace pdfium -#endif // TESTING_SCOPED_LOCALE_H_ +#endif // CORE_FXCRT_SCOPED_LOCALE_H_ diff --git a/core/fxcrt/widestring_unittest.cpp b/core/fxcrt/widestring_unittest.cpp index 8f546a7a05..8073bcc815 100644 --- a/core/fxcrt/widestring_unittest.cpp +++ b/core/fxcrt/widestring_unittest.cpp @@ -1436,7 +1436,7 @@ TEST(WideString, EqualsASCIINoCase) { } TEST(WideString, ToASCII) { - const char* kResult = + constexpr char kResult[] = "x" "\x02" "\x7f" @@ -1453,7 +1453,7 @@ TEST(WideString, ToASCII) { } TEST(WideString, ToLatin1) { - const char* kResult = + constexpr char kResult[] = "x" "\x82" "\xff" @@ -1472,7 +1472,7 @@ TEST(WideString, ToLatin1) { TEST(WideString, ToDefANSI) { EXPECT_EQ("", WideString().ToDefANSI()); #if BUILDFLAG(IS_WIN) - const char* kResult = + constexpr char kResult[] = "x" "?" "\xff" @@ -1480,7 +1480,7 @@ TEST(WideString, ToDefANSI) { "?" "y"; #else - const char* kResult = + constexpr char kResult[] = "x" "\x80" "\xff" diff --git a/core/fxcrt/zip.h b/core/fxcrt/zip.h index 6093bf89f9..e3260a04bb 100644 --- a/core/fxcrt/zip.h +++ b/core/fxcrt/zip.h @@ -34,8 +34,8 @@ namespace fxcrt { // // Differences include: // - Only zips together two or three views instead of N. -// - Size is determined by the first view, which must be smaller than the -// other view(s). +// - Size is determined by the first view, which must be smaller than or equal +// to the other view(s). // - Views are passed as forwarding-references, so non-const views may be // assigned-to in the body of the loop. // - Only those methods required to support use in a range-based for-loop diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn index 4e139163dd..a8914856a6 100644 --- a/core/fxge/BUILD.gn +++ b/core/fxge/BUILD.gn @@ -95,6 +95,8 @@ source_set("fxge") { "dib/cfx_imagetransformer.h", "dib/cfx_scanlinecompositor.cpp", "dib/cfx_scanlinecompositor.h", + "dib/cfx_scanlinecompositor_hwy.cpp", + "dib/cfx_scanlinecompositor_hwy.h", "dib/cstretchengine.cpp", "dib/cstretchengine.h", "dib/cstretchengine_hwy.cpp", @@ -250,11 +252,6 @@ source_set("fxge") { "skia/fx_skia_device.h", ] public_deps += [ "//skia" ] - - if (pdf_skia_font_manager_target != "") { - defines += [ "PDF_USE_SKIA_CUSTOM_FONT_MANAGER" ] - deps += [ pdf_skia_font_manager_target ] - } } visibility = [ "../../*" ] @@ -270,8 +267,8 @@ if (pdf_enable_fontations) { features = [ "agl" ] deps = [ "//build/rust:cxx_cppdeps", - "//third_party/rust/read_fonts/v0_43:lib", - "//third_party/rust/skrifa/v0_46:lib", + "//third_party/rust/read_fonts/v0_45:lib", + "//third_party/rust/skrifa/v0_48:lib", ] configs += [ ":skrifa_cxx_config" ] } @@ -304,6 +301,7 @@ source_set("test_support") { deps = [ ":fxge", "../fxcrt", + "../fxcrt:test_support", ] } diff --git a/core/fxge/cfx_fontmgr.cpp b/core/fxge/cfx_fontmgr.cpp index 1a18fb9cf5..1a9b2277c6 100644 --- a/core/fxge/cfx_fontmgr.cpp +++ b/core/fxge/cfx_fontmgr.cpp @@ -39,9 +39,6 @@ namespace { // otherwise get access to system fonts. sk_sp CreateSkiaFontManagerFallback( CFX_FontMgr::FontBackend backend) { -#if defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) - return pdfium_skia_custom_font_manager(); -#else // defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) #if defined(PDF_ENABLE_FONTATIONS) if (backend == CFX_FontMgr::FontBackend::kFontations) { // This is a SkFontMgr which will use Fontations to decode font data. @@ -54,7 +51,6 @@ sk_sp CreateSkiaFontManagerFallback( #else return nullptr; #endif // defined(PDF_ENABLE_FREETYPE) -#endif // defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) } // static diff --git a/core/fxge/cfx_fontmgr.h b/core/fxge/cfx_fontmgr.h index 16437c24a4..1b755f487f 100644 --- a/core/fxge/cfx_fontmgr.h +++ b/core/fxge/cfx_fontmgr.h @@ -74,8 +74,4 @@ class CFX_FontMgr { #endif }; -#if defined(PDF_USE_SKIA) && defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) -extern sk_sp pdfium_skia_custom_font_manager(); -#endif // defined(PDF_USE_SKIA) && defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) - #endif // CORE_FXGE_CFX_FONTMGR_H_ diff --git a/core/fxge/cfx_path.cpp b/core/fxge/cfx_path.cpp index 5495887e28..394f7793f9 100644 --- a/core/fxge/cfx_path.cpp +++ b/core/fxge/cfx_path.cpp @@ -17,6 +17,10 @@ namespace { +bool PointsFloatEqual(const CFX_PointF& p1, const CFX_PointF& p2) { + return FXSYS_IsFloatEqual(p1.x, p2.x) && FXSYS_IsFloatEqual(p1.y, p2.y); +} + bool IsRectPreTransform(const std::vector& points) { if (points.size() != 5 && points.size() != 4) { return false; @@ -26,12 +30,13 @@ bool IsRectPreTransform(const std::vector& points) { return false; } - if (points.size() == 5 && points[0].point_ != points[4].point_) { + if (points.size() == 5 && + !PointsFloatEqual(points[0].point_, points[4].point_)) { return false; } - if (points[0].point_ == points[2].point_ || - points[1].point_ == points[3].point_) { + if (PointsFloatEqual(points[0].point_, points[2].point_) || + PointsFloatEqual(points[1].point_, points[3].point_)) { return false; } @@ -44,7 +49,7 @@ bool IsRectPreTransform(const std::vector& points) { } bool XYBothNotEqual(const CFX_PointF& p1, const CFX_PointF& p2) { - return p1.x != p2.x && p1.y != p2.y; + return !FXSYS_IsFloatEqual(p1.x, p2.x) && !FXSYS_IsFloatEqual(p1.y, p2.y); } bool IsRectImpl(const std::vector& points) { @@ -73,7 +78,8 @@ CFX_FloatRect CreateRectFromPoints(const CFX_PointF& p1, const CFX_PointF& p2) { bool PathPointsNeedNormalization(const std::vector& points) { return points.size() > 5 || - (points.size() == 5 && points[0].point_ != points[4].point_); + (points.size() == 5 && + !PointsFloatEqual(points[0].point_, points[4].point_)); } // Returns whether `point` lies strictly between `prev` and `next` on a @@ -81,11 +87,13 @@ bool PathPointsNeedNormalization(const std::vector& points) { bool IsBetweenOnAxisAlignedLine(const CFX_PointF& prev, const CFX_PointF& point, const CFX_PointF& next) { - if (prev.y == point.y && point.y == next.y) { + if (FXSYS_IsFloatEqual(prev.y, point.y) && + FXSYS_IsFloatEqual(point.y, next.y)) { return (prev.x < point.x && point.x < next.x) || (prev.x > point.x && point.x > next.x); } - if (prev.x == point.x && point.x == next.x) { + if (FXSYS_IsFloatEqual(prev.x, point.x) && + FXSYS_IsFloatEqual(point.x, next.x)) { return (prev.y < point.y && point.y < next.y) || (prev.y > point.y && point.y > next.y); } @@ -96,7 +104,8 @@ std::vector GetNormalizedPoints( const std::vector& points) { DCHECK(PathPointsNeedNormalization(points)); - const size_t expected_size = points[0].point_ == points.back().point_ ? 5 : 4; + const size_t expected_size = + PointsFloatEqual(points[0].point_, points.back().point_) ? 5 : 4; if (expected_size == 4 && !points.back().close_figure_) { return {}; } @@ -118,7 +127,7 @@ std::vector GetNormalizedPoints( const auto next = std::next(it); if (!point.close_figure_) { // If the line does not move, skip this point. - if (point.point_ == normalized.back().point_) { + if (PointsFloatEqual(point.point_, normalized.back().point_)) { continue; } @@ -132,7 +141,7 @@ std::vector GetNormalizedPoints( continue; } } else if (next == points.end()) { - if (point.point_ == normalized.back().point_ || + if (PointsFloatEqual(point.point_, normalized.back().point_) || IsBetweenOnAxisAlignedLine(normalized.back().point_, point.point_, points[0].point_)) { normalized.back().close_figure_ = true; diff --git a/core/fxge/cfx_path_unittest.cpp b/core/fxge/cfx_path_unittest.cpp index 799889d8b2..9ca1979d64 100644 --- a/core/fxge/cfx_path_unittest.cpp +++ b/core/fxge/cfx_path_unittest.cpp @@ -84,9 +84,14 @@ TEST(CFXPath, MicroShearTransformTolerance) { const CFX_Matrix kMicroDeviationMatrix(1.0f, 0.0f, -4.69486423e-08f, 1.0f, 0.0f, 0.0f); EXPECT_TRUE(path.IsRect()); - // TODO(crbug.com/550515869): Update expectation to CFX_FloatRect(1, 2, 3, 5) - // once micro-shear tolerance is supported. - EXPECT_FALSE(path.GetRect(&kMicroDeviationMatrix).has_value()); + // Use EXPECT_FLOAT_EQ() instead of EXPECT_EQ() with `CFX_FloatRect` because + // `CFX_FloatRect` uses exact floating-point equality. + std::optional rect = path.GetRect(&kMicroDeviationMatrix); + ASSERT_TRUE(rect.has_value()); + EXPECT_FLOAT_EQ(1.0f, rect->left); + EXPECT_FLOAT_EQ(2.0f, rect->bottom); + EXPECT_FLOAT_EQ(3.0f, rect->right); + EXPECT_FLOAT_EQ(5.0f, rect->top); } TEST(CFXPath, FivePointRectFuzzyEndpoints) { @@ -96,10 +101,8 @@ TEST(CFXPath, FivePointRectFuzzyEndpoints) { path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); path.AppendPoint({1e-7f, -1e-7f}, CFX_Path::Point::Type::kLine); - // TODO(crbug.com/550515869): Update expectations to true and - // CFX_FloatRect(0, 0, 2, 1) once fuzzy endpoint matching is supported. - EXPECT_FALSE(path.IsRect()); - EXPECT_FALSE(path.GetRect(nullptr).has_value()); + EXPECT_TRUE(path.IsRect()); + EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetRect(nullptr)); } TEST(CFXPath, Hexagon) { diff --git a/core/fxge/dib/cfx_scanlinecompositor.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp index fd7864330f..b3f255951c 100644 --- a/core/fxge/dib/cfx_scanlinecompositor.cpp +++ b/core/fxge/dib/cfx_scanlinecompositor.cpp @@ -19,6 +19,7 @@ #include "core/fxcrt/stl_util.h" #include "core/fxcrt/zip.h" #include "core/fxge/dib/blend.h" +#include "core/fxge/dib/cfx_scanlinecompositor_hwy.h" #include "core/fxge/dib/fx_dib.h" #if defined(PDF_USE_LIBYUV) @@ -444,19 +445,18 @@ void CompositeRow_Bgr2Bgra_NoBlend_NoClip( int src_Bpp) { const size_t width = dest_span.size(); if (src_Bpp == 3) { - auto bgr_span = fxcrt::reinterpret_span>( - src_span.first(width * 3)); + auto bgr_span = + fxcrt::reinterpret_span>(src_span).first( + width); #if defined(PDF_USE_LIBYUV) const int pixel_count = pdfium::checked_cast(width); const uint8_t* src_bytes = pdfium::as_bytes(bgr_span).data(); uint8_t* dest_bytes = pdfium::as_writable_bytes(dest_span).data(); if constexpr (std::is_same_v>) { - libyuv::RGB24ToARGB(src_bytes, pixel_count * 3, dest_bytes, - pixel_count * 4, pixel_count, 1); + libyuv::RGB24ToARGB(src_bytes, 0, dest_bytes, 0, pixel_count, 1); } else { static_assert(std::is_same_v>); - libyuv::RAWToARGB(src_bytes, pixel_count * 3, dest_bytes, pixel_count * 4, - pixel_count, 1); + libyuv::RAWToARGB(src_bytes, 0, dest_bytes, 0, pixel_count, 1); } #else CopyRowToOpaqueBgra(dest_span, bgr_span); @@ -464,9 +464,16 @@ void CompositeRow_Bgr2Bgra_NoBlend_NoClip( return; } CHECK_EQ(src_Bpp, 4); - CopyRowToOpaqueBgra(dest_span, - fxcrt::reinterpret_span>( - src_span.first(width * 4))); + auto bgra_src = + fxcrt::reinterpret_span>(src_span).first( + width); + if constexpr (std::is_same_v>) { + // SIMD: packed-BGRA src/dest, opaque copy (alpha forced 0xFF) + CompositeRowBgra2BgrxForceOpaqueSimd(bgra_src, dest_span); + } else { + static_assert(std::is_same_v>); + CopyRowToOpaqueBgra(dest_span, bgra_src); + } } template @@ -653,16 +660,23 @@ void CompositeRowBgra2Bgra(pdfium::span> src_span, } return; } - for (auto [input, output] : fxcrt::Zip(src_span, dest_span)) { - const uint8_t src_alpha = - CompositePixelBgra2BgraCommon(input, /*clip=*/255, output); - if (src_alpha == 255) { - // Fully opaque source: AlphaUnion() would be 255, the ratio 255, and - // AlphaMerge(..., 255) returns the source, so the general path in - // CompositePixelBgra2BgraNoBlend() reduces to a plain copy. - CopyPixelToOpaqueBgra(input, output); - } else if (src_alpha != 0) { - CompositePixelBgra2BgraNoBlend(input, src_alpha, output); + if constexpr (std::is_same_v>) { + // SIMD: packed-BGRA, normal, no clip + CompositeRowBgra2BgraSimd(src_span, dest_span); + } else { + static_assert(std::is_same_v>); + for (auto [input, output] : fxcrt::Zip(src_span, dest_span)) { + const uint8_t src_alpha = + CompositePixelBgra2BgraCommon(input, /*clip=*/255, output); + if (src_alpha == 255) { + // Fully opaque source: CompositePixelBgra2BgraNoBlend() reduces to a + // plain copy. + CopyPixelToOpaqueBgra(input, output); + } else if (src_alpha != 0) { + CompositePixelBgra2BgraNoBlend(input, src_alpha, output); + } + // src_alpha == 0: invisible source, or dest already set by + // CompositePixelBgra2BgraCommon(). Nothing to do. } } return; @@ -828,6 +842,14 @@ void CompositeRow_Rgb2Rgb_NoBlend_NoClip(pdfium::span dest_span, FXSYS_memcpy(dest_scan, src_scan, width * dest_Bpp); return; } +#if defined(PDF_USE_LIBYUV) + if (src_Bpp == 3 && dest_Bpp == 4) { + // BGR24 -> BGRx expand; libyuv stamps 4th byte 0xFF vs scalar's + // untouched. kBgrx padding, non-displayed: no output impact. + libyuv::RGB24ToARGB(src_scan, width * 3, dest_scan, width * 4, width, 1); + return; + } +#endif for (int col = 0; col < width; col++) { FXSYS_memcpy(dest_scan, src_scan, 3); dest_scan += dest_Bpp; diff --git a/core/fxge/dib/cfx_scanlinecompositor_hwy.cpp b/core/fxge/dib/cfx_scanlinecompositor_hwy.cpp new file mode 100644 index 0000000000..d4f304f8a9 --- /dev/null +++ b/core/fxge/dib/cfx_scanlinecompositor_hwy.cpp @@ -0,0 +1,204 @@ +// Copyright 2026 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_scanlinecompositor_hwy.h" + +#include +#include + +#include "core/fxcrt/check_op.h" +#include "core/fxcrt/compiler_specific.h" +#include "core/fxcrt/span.h" +#include "core/fxcrt/span_util.h" + +#undef HWY_TARGET_INCLUDE +#define HWY_TARGET_INCLUDE "core/fxge/dib/cfx_scanlinecompositor_hwy.cpp" +#include "third_party/highway/src/hwy/foreach_target.h" // IWYU pragma: keep + +#include "third_party/highway/src/hwy/highway.h" + +HWY_BEFORE_NAMESPACE(); +namespace fxge_composite_simd { +namespace HWY_NAMESPACE { +namespace { + +namespace hn = hwy::HWY_NAMESPACE; + +// exact floor(x/255), x in [0, 65025] +template +HWY_INLINE V Div255(V x) { + const hn::DFromV d; + return hn::ShiftRight<8>( + hn::Add(hn::Add(x, hn::Set(d, 1)), hn::ShiftRight<8>(x))); +} + +// (back*(255-a) + src*a)/255; operands <= 255 -> sum <= 65025, Div255 exact +template +HWY_INLINE V AlphaMergeLanes(V back, V src, V alpha, V v255) { + return Div255( + hn::Add(hn::Mul(back, hn::Sub(v255, alpha)), hn::Mul(src, alpha))); +} + +// scalar tail; bit-exact with CompositeRowBgra2Bgra normal/no-clip +inline uint32_t CompositePixelBgra2Bgra(uint32_t src, uint32_t back) { + const uint32_t back_alpha = FXARGB_A(back); + if (back_alpha == 0) { + return src; + } + const uint32_t src_alpha = FXARGB_A(src); + if (src_alpha == 0) { + return back; + } + if (src_alpha == 255) { + return src; + } + const uint32_t dest_alpha = + back_alpha + src_alpha - (back_alpha * src_alpha / 255); + const uint32_t ratio = src_alpha * 255 / dest_alpha; + return ArgbEncode(dest_alpha, + AlphaMerge(FXARGB_R(back), FXARGB_R(src), ratio), + AlphaMerge(FXARGB_G(back), FXARGB_G(src), ratio), + AlphaMerge(FXARGB_B(back), FXARGB_B(src), ratio)); +} + +// Porter-Duff over, packed BGRA, normal, no clip. Per pixel: +// dest a == 0 -> copy src src a == 255 -> copy src +// src a == 0 -> keep dest else AlphaMerge/AlphaUnion over +// Byte spans bitcast to u32 lanes -> no 4-byte scanline alignment needed. +void RunBgra2Bgra(pdfium::span src, pdfium::span dest) { + const hn::ScalableTag d; + const hn::ScalableTag d8; + const size_t n = hn::Lanes(d); // pixels/vector + const auto m255 = hn::Set(d, 0xFF); // byte mask and the value 255 + const auto one = hn::Set(d, 1); + const auto zero = hn::Zero(d); + + size_t count = src.size() / 4; + while (count >= n) { + const auto s = hn::BitCast(d, hn::LoadU(d8, src.data())); + const auto back = hn::BitCast(d, hn::LoadU(d8, dest.data())); + const auto sa = hn::ShiftRight<24>(s); + const auto da = hn::ShiftRight<24>(back); + + const auto copy_src = hn::Or(hn::Eq(da, zero), hn::Eq(sa, m255)); + const auto keep_dest = hn::Eq(sa, zero); + + // all copy/keep -> skip divide; copy_src ? src : dest + if (hn::AllTrue(d, hn::Or(copy_src, keep_dest))) { + const auto result = hn::IfThenElse(copy_src, s, back); + hn::StoreU(hn::BitCast(d8, result), d8, dest.data()); + src = src.subspan(n * 4); + dest = dest.subspan(n * 4); + count -= n; + continue; + } + + // divisor forced >= 1: copy/keep lanes may yield da2 == 0 (unused) + const auto da2 = hn::Sub(hn::Add(da, sa), Div255(hn::Mul(da, sa))); + const auto ratio = hn::Div(hn::Mul(sa, m255), hn::Max(da2, one)); + + const auto db = hn::And(back, m255); + const auto dg = hn::And(hn::ShiftRight<8>(back), m255); + const auto dr = hn::And(hn::ShiftRight<16>(back), m255); + const auto sb = hn::And(s, m255); + const auto sg = hn::And(hn::ShiftRight<8>(s), m255); + const auto sr = hn::And(hn::ShiftRight<16>(s), m255); + const auto nb = AlphaMergeLanes(db, sb, ratio, m255); + const auto ng = AlphaMergeLanes(dg, sg, ratio, m255); + const auto nr = AlphaMergeLanes(dr, sr, ratio, m255); + const auto gen = + hn::Or(hn::Or(nb, hn::ShiftLeft<8>(ng)), + hn::Or(hn::ShiftLeft<16>(nr), hn::ShiftLeft<24>(da2))); + + auto result = hn::IfThenElse(keep_dest, back, gen); + result = hn::IfThenElse(copy_src, s, result); + hn::StoreU(hn::BitCast(d8, result), d8, dest.data()); + + src = src.subspan(n * 4); + dest = dest.subspan(n * 4); + count -= n; + } + for (size_t i = 0; i < count; ++i) { + const uint32_t s = FXARGB_GetDIB(src.first<4u>()); + const uint32_t back = FXARGB_GetDIB(dest.first<4u>()); + FXARGB_SetDIB(dest.first<4u>(), CompositePixelBgra2Bgra(s, back)); + src = src.subspan(4u); + dest = dest.subspan(4u); + } +} + +// Opaque BGRA(4) -> BGRA, alpha forced 0xFF (src alpha ignored). Same byte +// order required: src and dest both packed BGRA. +void RunBgra2BgrxForceOpaque(pdfium::span src, + pdfium::span dest) { + const hn::ScalableTag d; + const hn::ScalableTag d8; + const size_t n = hn::Lanes(d); + const auto alpha_ff = hn::Set(d, 0xFF000000u); + + size_t count = src.size() / 4; + while (count >= n) { + const auto v = hn::BitCast(d, hn::LoadU(d8, src.data())); + hn::StoreU(hn::BitCast(d8, hn::Or(v, alpha_ff)), d8, dest.data()); + src = src.subspan(n * 4); + dest = dest.subspan(n * 4); + count -= n; + } + for (size_t i = 0; i < count; ++i) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 0xFF; + src = src.subspan(4u); + dest = dest.subspan(4u); + } +} + +} // namespace +} // namespace HWY_NAMESPACE +} // namespace fxge_composite_simd +HWY_AFTER_NAMESPACE(); + +#if HWY_ONCE +namespace fxge_composite_simd { + +HWY_EXPORT(RunBgra2Bgra); +HWY_EXPORT(RunBgra2BgrxForceOpaque); + +// Unqualified name, in-namespace: a qualified name breaks single-target +// HWY_STATIC_DISPATCH. +void DispatchBgra2Bgra(pdfium::span src, + pdfium::span dest) { + // SAFETY: dispatch indexes Highway's per-target table; no span macro form. + UNSAFE_BUFFERS(HWY_DYNAMIC_DISPATCH(RunBgra2Bgra)(src, dest)); +} + +void DispatchBgra2BgrxForceOpaque(pdfium::span src, + pdfium::span dest) { + // SAFETY: see DispatchBgra2Bgra(). + UNSAFE_BUFFERS(HWY_DYNAMIC_DISPATCH(RunBgra2BgrxForceOpaque)(src, dest)); +} + +} // namespace fxge_composite_simd + +void CompositeRowBgra2BgraSimd( + pdfium::span> src_span, + pdfium::span> dest_span) { + // `src_span.size()` pixels, like `fxcrt::Zip(src_span, dest_span)`. + CHECK_LE(src_span.size(), dest_span.size()); + fxge_composite_simd::DispatchBgra2Bgra( + fxcrt::reinterpret_span(src_span), + fxcrt::reinterpret_span(dest_span)); +} + +void CompositeRowBgra2BgrxForceOpaqueSimd( + pdfium::span> src_span, + pdfium::span> dest_span) { + // `src_span.size()` pixels, like `fxcrt::Zip(src_span, dest_span)`. + CHECK_LE(src_span.size(), dest_span.size()); + fxge_composite_simd::DispatchBgra2BgrxForceOpaque( + fxcrt::reinterpret_span(src_span), + fxcrt::reinterpret_span(dest_span)); +} +#endif // HWY_ONCE diff --git a/core/fxge/dib/cfx_scanlinecompositor_hwy.h b/core/fxge/dib/cfx_scanlinecompositor_hwy.h new file mode 100644 index 0000000000..909c0ed443 --- /dev/null +++ b/core/fxge/dib/cfx_scanlinecompositor_hwy.h @@ -0,0 +1,26 @@ +// Copyright 2026 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_DIB_CFX_SCANLINECOMPOSITOR_HWY_H_ +#define CORE_FXGE_DIB_CFX_SCANLINECOMPOSITOR_HWY_H_ + +#include + +#include "core/fxcrt/span.h" +#include "core/fxge/dib/fx_dib.h" + +// Highway kernels for the scanline-compositor cases, each bit-exact with +// the scalar reference it replaces. Runtime target selection is internal. + +// SIMD replacement for CompositeRowBgra2Bgra(), normal blend, no clip. +void CompositeRowBgra2BgraSimd( + pdfium::span> src_span, + pdfium::span> dest_span); + +// SIMD replacement for CopyRowToOpaqueBgra(), BGRA source. +void CompositeRowBgra2BgrxForceOpaqueSimd( + pdfium::span> src_span, + pdfium::span> dest_span); + +#endif // CORE_FXGE_DIB_CFX_SCANLINECOMPOSITOR_HWY_H_ diff --git a/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp b/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp index cb2ba0d8e3..b0622a7175 100644 --- a/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp +++ b/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp @@ -7,10 +7,13 @@ #include #include +#include #include "build/build_config.h" #include "core/fxcrt/span.h" #include "core/fxcrt/stl_util.h" +#include "core/fxcrt/zip.h" +#include "core/fxge/dib/cfx_scanlinecompositor_hwy.h" #include "core/fxge/dib/fx_dib.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -102,6 +105,77 @@ void RunPreMultiplyTest( } #endif // defined(PDF_USE_SKIA) +// Independent re-derivation of CompositeRowBgra2BgraSimd's contract +// (AlphaUnion()/AlphaMerge(), fx_dib.h); not copied from the kernel. +FX_BGRA_STRUCT RefBgra2Bgra(const FX_BGRA_STRUCT& src, + const FX_BGRA_STRUCT& back) { + if (back.alpha == 0) { + return src; + } + if (src.alpha == 0) { + return back; + } + const int dest_alpha = back.alpha + src.alpha - back.alpha * src.alpha / 255; + const int ratio = src.alpha * 255 / dest_alpha; + return { + .blue = static_cast(AlphaMerge(back.blue, src.blue, ratio)), + .green = static_cast(AlphaMerge(back.green, src.green, ratio)), + .red = static_cast(AlphaMerge(back.red, src.red, ratio)), + .alpha = static_cast(dest_alpha), + }; +} + +void CheckBgra2Bgra(size_t width) { + // alpha cycle; dest phase `(i / 4) % 4` + static constexpr std::array kAlphaCycle = {0, 255, 100, 200}; + std::vector> src(width); + std::vector> dest(width); + for (size_t i = 0; i < width; ++i) { + // per 32 px: [0,16) src a in {0,255} -> all-copy/keep fast path; + // [16,32) mid a -> divide path, incl. mid-over-mid + const uint8_t src_a = (i % 32 < 16) + ? static_cast((i & 1) ? 255 : 0) + : kAlphaCycle[i % 4]; + src[i] = {.blue = static_cast(i * 7 + 1), + .green = static_cast(i * 13 + 2), + .red = static_cast(i * 29 + 3), + .alpha = src_a}; + dest[i] = {.blue = static_cast(i * 31 + 5), + .green = static_cast(i * 17 + 7), + .red = static_cast(i * 11 + 9), + .alpha = kAlphaCycle[(i / 4) % 4]}; + } + std::vector> expected(width); + for (auto [input, back, want] : fxcrt::Zip(src, dest, expected)) { + want = RefBgra2Bgra(input, back); + } + CompositeRowBgra2BgraSimd(src, dest); + for (size_t i = 0; i < width; ++i) { + EXPECT_EQ(expected[i], dest[i]) << "width " << width << " pixel " << i; + } +} + +void CheckBgra2BgrxForceOpaque(size_t width) { + std::vector> src(width); + for (size_t i = 0; i < width; ++i) { + src[i] = {.blue = static_cast(i * 5 + 1), + .green = static_cast(i * 9 + 2), + .red = static_cast(i * 19 + 3), + .alpha = static_cast(i * 3 + 4)}; // src alpha ignored + } + // sentinel dest; expect src.rgb, alpha 0xFF + std::vector> dest( + width, {.blue = 0xAA, .green = 0xAA, .red = 0xAA, .alpha = 0xAA}); + CompositeRowBgra2BgrxForceOpaqueSimd(src, dest); + for (size_t i = 0; i < width; ++i) { + const FX_BGRA_STRUCT want = {.blue = src[i].blue, + .green = src[i].green, + .red = src[i].red, + .alpha = 255}; + EXPECT_EQ(want, dest[i]) << "width " << width << " pixel " << i; + } +} + } // namespace namespace fxge { @@ -910,3 +984,50 @@ TEST(ScanlineCompositorTest, CompositeRgbBitmapLineBgrToBgraNormal) { EXPECT_THAT(dest_scan, ElementsAreArray(kExpectedRgba)); } } + +// Vector body + sub-vector tail across widths spanning the SSE4/AVX2/AVX-512 +// lane counts (4/8/16). +TEST(ScanlineCompositorSimdTest, Bgra2BgraMatchesScalar) { + for (size_t width : {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 15u, + 16u, 17u, 31u, 32u, 33u, 63u, 64u, 65u, 100u, 257u}) { + CheckBgra2Bgra(width); + } +} + +// Opaque copy: rgb preserved, alpha forced 0xFF, across vector/tail widths. +TEST(ScanlineCompositorSimdTest, Bgra2BgrxForceOpaqueMatchesScalar) { + for (size_t width : {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 15u, + 16u, 17u, 31u, 32u, 33u, 63u, 64u, 65u, 100u, 257u}) { + CheckBgra2BgrxForceOpaque(width); + } +} + +// BGR24 -> BGRx expand via public path. RGB copied verbatim; padding byte +// 0xFF under libyuv, untouched by scalar (kBgrx padding, not displayed). +TEST(ScanlineCompositorSimdTest, CompositeRgbBitmapLineBgr24ToBgrx) { + constexpr int kWidth = 67; // non-multiple-of-16: exercises libyuv tail + + std::vector src(static_cast(kWidth) * 3); + for (size_t i = 0; i < src.size(); ++i) { + src[i] = static_cast(i * 7 + 1); + } + constexpr uint8_t kPad = 0x3C; // sentinel: 0xFF-stamp vs left-as-is + std::vector dest(static_cast(kWidth) * 4, kPad); + + CFX_ScanlineCompositor compositor; + ASSERT_TRUE(compositor.Init(FXDIB_Format::kBgrx, FXDIB_Format::kBgr, + /*src_palette=*/{}, /*mask_color=*/0, + BlendMode::kNormal, /*bRgbByteOrder=*/false)); + compositor.CompositeRgbBitmapLine(dest, src, kWidth, /*clip_scan=*/{}); + + for (int i = 0; i < kWidth; ++i) { + EXPECT_EQ(dest[i * 4 + 0], src[i * 3 + 0]) << "blue @ " << i; + EXPECT_EQ(dest[i * 4 + 1], src[i * 3 + 1]) << "green @ " << i; + EXPECT_EQ(dest[i * 4 + 2], src[i * 3 + 2]) << "red @ " << i; +#if defined(PDF_USE_LIBYUV) + EXPECT_EQ(dest[i * 4 + 3], 0xFF) << "padding @ " << i; // libyuv 0xFF +#else + EXPECT_EQ(dest[i * 4 + 3], kPad) << "padding @ " << i; // scalar leaves it +#endif + } +} diff --git a/core/fxge/display_list_device_driver.cpp b/core/fxge/display_list_device_driver.cpp index c24f62ecbf..1f5622f457 100644 --- a/core/fxge/display_list_device_driver.cpp +++ b/core/fxge/display_list_device_driver.cpp @@ -11,6 +11,7 @@ #include "core/fxcrt/bytestring.h" #include "core/fxcrt/check_op.h" #include "core/fxcrt/fx_coordinates.h" +#include "core/fxcrt/scoped_locale.h" #include "core/fxcrt/span.h" #include "core/fxge/cfx_fillrenderoptions.h" #include "core/fxge/cfx_font.h" @@ -180,6 +181,7 @@ bool DisplayListDeviceDriver::SetClip_PathFill( const CFX_Path& path, const CFX_Matrix* pObject2Device, const CFX_FillRenderOptions& fill_options) { + pdfium::ScopedLocale scoped_locale("C"); const char* rule = FormatFillType(fill_options.fill_type); ByteString mat_str = pObject2Device ? FormatMatrix(*pObject2Device) : ByteString("none"); @@ -193,6 +195,7 @@ bool DisplayListDeviceDriver::SetClip_PathStroke( const CFX_Path& path, const CFX_Matrix* pObject2Device, const CFX_GraphStateData* pGraphState) { + pdfium::ScopedLocale scoped_locale("C"); ByteString mat_str = pObject2Device ? FormatMatrix(*pObject2Device) : ByteString("none"); display_list_ += @@ -209,6 +212,7 @@ bool DisplayListDeviceDriver::DrawPath( uint32_t stroke_color, bool group_knockout, const CFX_FillRenderOptions& fill_options) { + pdfium::ScopedLocale scoped_locale("C"); ByteString mat_str = pObject2Device ? FormatMatrix(*pObject2Device) : ByteString("none"); ByteString stroke_str; @@ -242,6 +246,7 @@ bool DisplayListDeviceDriver::FillRect(const FX_RECT& rect, bool DisplayListDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo, const CFX_PointF& ptLineTo, uint32_t color) { + pdfium::ScopedLocale scoped_locale("C"); display_list_ += ByteString::Format( "DrawCosmeticLine (%.4f,%.4f)->(%.4f,%.4f) color=0x%08x\n", ptMoveTo.x, ptMoveTo.y, ptLineTo.x, ptLineTo.y, color); @@ -290,6 +295,7 @@ RenderDeviceDriverIface::StartResult DisplayListDeviceDriver::StartDIBits( const CFX_Matrix& matrix, const FXDIB_ResampleOptions& options, BlendMode blend_type) { + pdfium::ScopedLocale scoped_locale("C"); display_list_ += ByteString({"StartDIBits ", FormatDIBInfo(*bitmap).AsStringView(), " matrix=", FormatMatrix(matrix).AsStringView(), @@ -305,6 +311,7 @@ bool DisplayListDeviceDriver::DrawDeviceText( float font_size, uint32_t color, const CFX_TextRenderOptions& options) { + pdfium::ScopedLocale scoped_locale("C"); ByteString font_name = font ? font->GetFamilyName() : ByteString(); if (font_name.IsEmpty() && font) { font_name = font->GetPsName(); @@ -326,6 +333,7 @@ bool DisplayListDeviceDriver::DrawDeviceText( } bool DisplayListDeviceDriver::MultiplyAlpha(float alpha) { + pdfium::ScopedLocale scoped_locale("C"); display_list_ += ByteString::Format("MultiplyAlpha alpha=%.4f\n", alpha); return true; } diff --git a/core/fxge/skrifa/src/main.rs b/core/fxge/skrifa/src/main.rs index d6ad384626..346ad3e636 100644 --- a/core/fxge/skrifa/src/main.rs +++ b/core/fxge/skrifa/src/main.rs @@ -4,7 +4,6 @@ #![allow(dead_code)] use read_fonts::{ - model::pen::OutlinePen, ps::{ cff::{ charset::Charset as CffCharset, CffFontRef, Encoding as CffEncoding, @@ -23,7 +22,9 @@ use skrifa::{ charmap::Charmap, instance::{LocationRef, Size}, metrics::Metrics, - outline::{DrawSettings, Engine, HintingInstance, HintingOptions, OutlineGlyphFormat}, + outline::{ + DrawSettings, Engine, HintingInstance, HintingOptions, OutlineGlyphFormat, OutlinePen, + }, string::StringId, FontRef, GlyphNameSource, GlyphNames, MetadataProvider, OutlineGlyphCollection, }; @@ -300,7 +301,7 @@ impl SkrifaFont<'_> { Self::Cff(cff) => { if cff.font.is_cid() { let charset = cff.charset.as_ref()?; - let actual_gid = charset.glyph_id(Sid::new(gid as u16)).ok()?; + let actual_gid = charset.glyph_id(Sid::new(gid as u16))?; Some(actual_gid.to_u32()) } else { Some(gid) @@ -452,7 +453,7 @@ impl SkrifaFont<'_> { Self::Cff(cff) if cff.font.is_cid() => cff .charset .as_ref() - .and_then(|charset| charset.glyph_id(Sid::new(cid)).ok()) + .and_then(|charset| charset.glyph_id(Sid::new(cid))) .unwrap_or_default() .to_u32(), _ => 0, @@ -947,7 +948,7 @@ impl SkrifaFont<'_> { Self::Cff(cff) if cff.charset.is_some() && !cff.font.is_cid() => cff .charset .as_ref() - .and_then(|charset| charset.string_id(gid.into()).ok()) + .and_then(|charset| charset.string_id(gid.into())) .and_then(|sid| cff.font.string(sid)) .and_then(|s| core::str::from_utf8(s).ok()) .map(|s| s.to_string()) diff --git a/core/fxge/win32/cgdi_display_driver.cpp b/core/fxge/win32/cgdi_display_driver.cpp index 2269e3b558..1ccdd2040b 100644 --- a/core/fxge/win32/cgdi_display_driver.cpp +++ b/core/fxge/win32/cgdi_display_driver.cpp @@ -39,7 +39,7 @@ bool CGdiDisplayDriver::GetDIBits(RetainPtr bitmap, BitBlt(hDCMemory, 0, 0, width, height, dc_handle_, left, top, SRCCOPY); SelectObject(hDCMemory, holdbmp); BITMAPINFO bmi = {}; - bmi.bmiHeader.biSize = sizeof bmi.bmiHeader; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biBitCount = bitmap->GetBPP(); bmi.bmiHeader.biHeight = -height; bmi.bmiHeader.biPlanes = 1; diff --git a/core/fxge/win32/cgdi_plus_ext.cpp b/core/fxge/win32/cgdi_plus_ext.cpp index 880b3abb5c..5b94a3b1b2 100644 --- a/core/fxge/win32/cgdi_plus_ext.cpp +++ b/core/fxge/win32/cgdi_plus_ext.cpp @@ -12,16 +12,13 @@ #include #include -#include #include #include #include "core/fxcrt/check_op.h" #include "core/fxcrt/compiler_specific.h" -#include "core/fxcrt/fx_memcpy_wrappers.h" #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/fxcrt/numerics/safe_conversions.h" #include "core/fxcrt/span.h" @@ -65,13 +62,7 @@ enum { FuncId_GdipDrawImagePointsI, FuncId_GdiplusStartup, FuncId_GdipDrawLineI, - FuncId_GdipCreatePath, FuncId_GdipSetPathFillMode, - FuncId_GdipSetClipRegion, - FuncId_GdipWidenPath, - FuncId_GdipAddPathLine, - FuncId_GdipAddPathRectangle, - FuncId_GdipDeleteRegion, FuncId_GdipSetPenLineCap197819, FuncId_GdipSetPenDashOffset, FuncId_GdipCreateMatrix2, @@ -102,13 +93,7 @@ LPCSTR g_GdipFuncNames[] = { "GdipDrawImagePointsI", "GdiplusStartup", "GdipDrawLineI", - "GdipCreatePath", "GdipSetPathFillMode", - "GdipSetClipRegion", - "GdipWidenPath", - "GdipAddPathLine", - "GdipAddPathRectangle", - "GdipDeleteRegion", "GdipSetPenLineCap197819", "GdipSetPenDashOffset", "GdipCreateMatrix2", @@ -155,18 +140,8 @@ 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 = @@ -372,153 +347,6 @@ std::optional> IsSmallTriangle( return std::nullopt; } -class GpStream final : public IStream { - public: - GpStream() = default; - ~GpStream() = default; - - // IUnknown - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, - void** ppvObject) override { - if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) || - iid == __uuidof(ISequentialStream)) { - *ppvObject = static_cast(this); - AddRef(); - return S_OK; - } - return E_NOINTERFACE; - } - ULONG STDMETHODCALLTYPE AddRef() override { - return (ULONG)InterlockedIncrement(&ref_count_); - } - ULONG STDMETHODCALLTYPE Release() override { - ULONG res = (ULONG)InterlockedDecrement(&ref_count_); - if (res == 0) { - delete this; - } - return res; - } - - // ISequentialStream - HRESULT STDMETHODCALLTYPE Read(void* output, - ULONG cb, - ULONG* pcbRead) override { - if (pcbRead) { - *pcbRead = 0; - } - - if (read_pos_ >= inter_stream_.tellp()) { - return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA); - } - - size_t bytes_left = pdfium::checked_cast( - std::streamoff(inter_stream_.tellp()) - read_pos_); - size_t bytes_out = std::min(pdfium::checked_cast(cb), bytes_left); - UNSAFE_TODO(FXSYS_memcpy(output, inter_stream_.str().c_str() + read_pos_, - bytes_out)); - read_pos_ += bytes_out; - if (pcbRead) { - *pcbRead = (ULONG)bytes_out; - } - - return S_OK; - } - HRESULT STDMETHODCALLTYPE Write(const void* input, - ULONG cb, - ULONG* pcbWritten) override { - if (cb <= 0) { - if (pcbWritten) { - *pcbWritten = 0; - } - return S_OK; - } - inter_stream_.write(reinterpret_cast(input), cb); - if (pcbWritten) { - *pcbWritten = cb; - } - return S_OK; - } - - // IStream - HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override { - return E_NOTIMPL; - } - HRESULT STDMETHODCALLTYPE CopyTo(IStream*, - ULARGE_INTEGER, - ULARGE_INTEGER*, - ULARGE_INTEGER*) override { - return E_NOTIMPL; - } - HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, - ULARGE_INTEGER, - DWORD) override { - return E_NOTIMPL; - } - HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, - ULARGE_INTEGER, - DWORD) override { - return E_NOTIMPL; - } - HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override { - return E_NOTIMPL; - } - HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, - DWORD dwOrigin, - ULARGE_INTEGER* lpNewFilePointer) override { - std::streamoff start; - std::streamoff new_read_position; - switch (dwOrigin) { - case STREAM_SEEK_SET: - start = 0; - break; - case STREAM_SEEK_CUR: - start = read_pos_; - break; - case STREAM_SEEK_END: - if (inter_stream_.tellp() < 0) { - return STG_E_SEEKERROR; - } - start = inter_stream_.tellp(); - break; - default: - return STG_E_INVALIDFUNCTION; - } - new_read_position = start + liDistanceToMove.QuadPart; - if (new_read_position > inter_stream_.tellp()) { - return STG_E_SEEKERROR; - } - - read_pos_ = new_read_position; - if (lpNewFilePointer) { - lpNewFilePointer->QuadPart = read_pos_; - } - - return S_OK; - } - HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, - DWORD grfStatFlag) override { - if (!pStatstg) { - return STG_E_INVALIDFUNCTION; - } - - UNSAFE_TODO(ZeroMemory(pStatstg, sizeof(STATSTG))); - - if (inter_stream_.tellp() < 0) { - return STG_E_SEEKERROR; - } - - pStatstg->cbSize.QuadPart = inter_stream_.tellp(); - return S_OK; - } - - private: - LONG ref_count_ = 1; - std::streamoff read_pos_ = 0; - fxcrt::ostringstream inter_stream_; -}; - } // namespace CGdiplusExt::CGdiplusExt() = default; diff --git a/docs/embedpdf/syncs/2026-11-01-5f092b529.md b/docs/embedpdf/syncs/2026-11-01-5f092b529.md new file mode 100644 index 0000000000..ecaedfb343 --- /dev/null +++ b/docs/embedpdf/syncs/2026-11-01-5f092b529.md @@ -0,0 +1,176 @@ +# Sync 2026-11-01: upstream 2b2d56c79..5f092b529 (39 commits) + +## Summary + +Upstream from 2026-10-27 to 2026-10-30, merged into `4e4a8eaef`. + +Upstream converted its remaining embedder tests to display lists, rolled its toolchain (build, +clang, rust, libc++, abseil, PartitionAlloc) and Skia, compiles the Brotli decoder by default, +vectorized the scanline compositor, and fixed the major version in saved headers. We point our +tests at the display lists (our own dumps where our appearance generator draws differently), leave +Brotli out of our release builds (its switch per thread where it is built), and build wasm32 +without libyuv, whose new SIMD code crashes emsdk 3.1.72's compiler. + +## Likely security fixes + +- `8e5483c62` 2026-10-27 Use fuzzy float equality in CFX_Path::GetRect() + +Taken as upstream wrote it. Not a security fix: a speed-up. Clip paths with a microscopic shear +count as rectangles again, so AGG clips by box instead of by polygon mask (85% less render CPU on +upstream's test page). + +## Conflicts (21 files, 0 blocks) + +A block with an `EPDF_TLS` count is never resolved by taking upstream's side without putting `EPDF_TLS` back (rule thread-confined-globals). + +| File | Blocks | Rules in the blocks | EPDF_TLS | Upstream commits | Resolution | +| :-- | --: | :-- | --: | :-- | :-- | +| `testing/resources/embedder_tests/annotation_ink_multiple_agg.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did. Our ink appearance differs on every platform, so the display list `annotation_ink_multiple.dump.txt` is ours (see below). | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_modified_image_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_modified_path_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_modified_text_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_new_annot_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_new_image_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_new_text_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annotation_stamp_with_ap_two_paths_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annots_page2_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/annots_page2_modified_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/bug_1558_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/form_object_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/form_object_scaled_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/freetext_annotation_without_da_agg.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did. Our free text appearance differs on every platform, so `freetext_annotation_without_da.dump.txt` is ours (see below). | +| `testing/resources/embedder_tests/freetext_annotation_without_da_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/freetext_annotation_without_da_modified_agg.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did. As above: `freetext_annotation_without_da_modified.dump.txt` is ours. | +| `testing/resources/embedder_tests/freetext_annotation_without_da_modified_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/hello_world_added_paths_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/hello_world_with_changed_text_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/text_in_page_marked_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | +| `testing/resources/embedder_tests/text_in_page_marked_non_primes_agg_mac.png` | binary, rename or delete | untagged | | `efb720209` Convert remaining embedder tests to display list comparisons || Deleted, as upstream did: the test compares a display list now, and it matches ours. | + +## Write audit (0 lines) + +None. + +## Upstream changes outside the conflicts + +- **Embedder tests on display lists** (`efb720209`, `181518ea0`, `0f3d165be`, `57e9a2667`, + `87bd48726`, `a5a0f99ff`). Our tests follow: + - `EPDFViewEmbedderTest.EPDFDocSetPageRotationByObjectNumber` used upstream's deleted + `rectangles_rotated` PNG; it compares the display list now, as upstream's own rotation test does. + - `FPDFAnnotEmbedderTest.ExtractInkMultiple` and `SetFontColor` draw with our appearance + generator (rule own-appearance-generator): round caps and joins and a stroke-fitted box for ink, + a different first-line inset for free text. Their dumps are ours, written with + `--write-results` and tagged in the tests; the rest of each display list matches upstream's. +- **Saved headers carry the major version (`d36b525a8`, test `17e04655e`):** a PDF 2.0 file saves as + `%PDF-2.0`, no longer `%PDF-1.0`. Merged into our `cpdf_creator.cpp`. +- **Free xref entries (`7a364ab9b`):** upstream's new test records its xref with gaps, with a TODO + (crbug.com/42271313) to remove them. Our complete xref (#86, rule complete-xref) already writes + one subsection with the free entries linked, so the test expects ours. +- **Arabic `/ActualText` orderings (`64df52afe`):** a new test of four ways to write the same word. + Upstream extracts one right; the fork none. With our `/ActualText` rules (bug-fix: a span is + reversed only after literal right-to-left text, which keeps Acrobat's shaped runs right), + variant 2's spans come out in visual order, where upstream reverses them. The test records the fork's results; + ordering a line's spans by position would fix both. See Open questions. +- **Brotli compiled in by default (`5b8d8c787`).** `/BrotliDecode` streams decode only when start-up + passes config version 6 with `m_BrotliEnabled`; our threads start through `EPDF_InitThread` + without a config, so it would never run. Our release builds set `pdf_enable_brotli=false` + (`build-target.sh`): the darwin-arm64 library is 167,968 bytes smaller (9,996,480 to 9,828,512), + the wasm32 archive 189,292 bytes; exports unchanged. Test builds keep it, so its tests run, and + its switch is per thread there (see Globals). Turning it on later: drop the argument and pass the + config through `EPDF_InitThread`. +- **Scanline compositor vectorized (`4e36424cb`):** two Highway kernels with runtime dispatch, byte + for byte the scalar result, and libyuv for the BGR24 to BGRx expand. +- **`CPDF_SyntaxParser::pool_` is used (`6876f741c`):** parsed names are interned. Our save reader + still clears its pool between objects (save-traversal). +- **Near-axis rectangles (`8e5483c62`)**, see Likely security fixes; `ScopedLocale` moved to + `core/fxcrt` (`ae6ae3ca9`); lcms 2.19.1 (`55ed4ec42`). +- Not in our builds: GDI+ removal (`2dbda71a1`), the `CGdiDisplayDriver` nit (`83adc17d6`), XFA + (`f47593101`, `5f092b529`), PartitionAlloc for Skia builds (`320708bfb`; we set + `pdf_use_partition_alloc=false`), the removed `pdf_skia_font_manager_target` (`4cd9288dd`; we + never set it). + +## Upstream appearance generator + +None. + +## Globals + +`check-globals.py` on the darwin-arm64 release build found one: `g_brotli_enabled`, Brotli's +switch, written by `FPDF_InitLibraryWithConfig`. Library start-up runs on every thread (rule +per-thread-init), so a shared flag would be written by each thread's start-up, and a start-up +without a config would turn it off for all. It is `EPDF_TLS` now (thread-confined-globals); ok after +that (263 shared writable, 26 thread-local). Release builds now leave Brotli out (25 thread-local). + +## Build and patches + +- `DEPS` changed in 8 commits: `build`, clang and rust (`dbd701727`), abseil, buildtools and libc++ + (`df710bcbf`), PartitionAlloc (`32aa2c3aa`), rust crates (`864b2ebdd`), rust test scripts + (`b435cef44`), Skia (`c0cb3e496`, 368 commits), libyuv (`ac04d23ea`), and reclient removed + (`b69c9b18a`). Synced with `gclient sync --no-history --shallow`; the new clang builds our code + without changes. +- All of `patches/embedpdf-runtime/` apply: every target's patches dry-run against clean sources + (some hunks shifted), and darwin-arm64 and wasm32 build. +- **wasm32 builds without libyuv** (`build-target.sh`: `pdf_use_libyuv=false`). The libyuv roll adds + WASM SIMD rows, and emsdk 3.1.72's clang (CI pins the same) crashes on + `I422ToARGBRow_WASMSIMD` ("Do not know how to split this operator's operand"). PDFium's own loops + write the same colors; only the unused byte of a BGRx pixel isn't forced to 0xFF, and the engine + renders into BGRA. linuxmusl-arm64 already builds without libyuv (S9). + +Build arguments added or changed: +- `build_overrides/pdfium.gni`: `pdf_enable_brotli_override = true` (was false); our release builds + set `pdf_enable_brotli=false` (`build-target.sh`). +- `build-target.sh`, wasm32: `pdf_use_libyuv=false`. +- `pdfium.gni`: `pdf_skia_font_manager_target` removed. + +## API diff + +None. The darwin-arm64 exports match the public headers, as in S14. + +## Tests + +- [x] fork unit + embedder tests (darwin-arm64): 1315 unit tests pass. Embedder: 1758 tests, + 9 skipped, 12 failing, every one "Pixels do not match" against a Mac PNG golden and all on S14's + list; S14's other 18 compare display lists now and pass. Linux runs in the PR's CI. + The 12, as the baseline for the next sync: + - `EPDFMeasureEmbedderTest.CaptionAppearanceGolden` + - `FPDFEditEmbedderTest.SetTextRenderMode` + - `FPDFProgressiveRenderEmbedderTest.RenderAnnotWithPause` + - `FPDFProgressiveRenderEmbedderTest.RenderFormsWithPause` + - `FPDFProgressiveRenderEmbedderTest.RenderHighlightWithColorScheme` + - `FPDFProgressiveRenderEmbedderTest.RenderHighlightWithColorSchemeAndConvertFillToStroke` + - `FPDFProgressiveRenderEmbedderTest.RenderStampWithColorScheme` + - `FPDFProgressiveRenderEmbedderTest.RenderTextWithColorScheme` + - `FPDFProgressiveRenderEmbedderTest.RenderWithPause` + - `FPDFProgressiveRenderEmbedderTest.RenderWithoutPause` + - `FPDFViewEmbedderTest.RenderBug664284WithNoNativeText` + - `FPDFViewEmbedderTest.RenderHelloWorldWithFlags` +- [x] layer mode: exit 0, no warnings; 213 layer-only failures, all expected; 185 crashes as in S14; + 288 writers. `EPDFFlattenEmbedderTest.FlattenMergedWidgetRemovesFieldTreeEntry` leaves 11 objects + in the layer instead of 12 (the count is informational; it passes in both modes). (A Mac + recording drops the Linux-only `TransFormWithClipAndSaveWithLocale`; its line stays.) +- [x] tag check (`--base 5f092b529`): 1006 blocks, 0 untagged. +- [x] C API check: ok, 587 functions in 24 headers. +- [x] globals check: ok after `g_brotli_enabled` became thread-local. +- [x] wasm build: builds locally, without libyuv. +- [ ] ThreadSanitizer soak: in the PR's CI. +- [ ] engine tests and engine-conformance with the local runtime: not run for this sync. +- changed expectations, each with its reason: + - `CPDFCreatorEmbedderTest.SingleSubsectionXrefWithFreeObjectLinkedList`: our complete xref + (complete-xref) instead of upstream's subsections with gaps. + - `FPDFTextEmbedderTest.ArabicActualTextOrdering`: the fork's extraction of the four variants + (bug-fix), see above. + - `annotation_ink_multiple.dump.txt`, `freetext_annotation_without_da.dump.txt`, + `freetext_annotation_without_da_modified.dump.txt`: our appearance generator's output + (own-appearance-generator). + - `EPDFViewEmbedderTest.EPDFDocSetPageRotationByObjectNumber`: upstream's `rectangles_rotated` + display list instead of its deleted PNG. + - Layer-mode writers: `FlattenMergedWidgetRemovesFieldTreeEntry` 12 to 11. + +## Ledger changes proposed + +None. + +## Open questions + +1. **Arabic `/ActualText`, variant 2 (spans in visual order).** Upstream gets it right, the fork + doesn't. Fixing it means ordering a line's spans by position (the RTL plan's work). diff --git a/docs/infra.md b/docs/infra.md new file mode 100644 index 0000000000..9baf224949 --- /dev/null +++ b/docs/infra.md @@ -0,0 +1,69 @@ +# PDFium Infrastructure + +[TOC] + +This document describes PDFium's continuous integration (CI), Commit Queue (CQ), +and build configuration infrastructure. + +## Waterfall + +The current health of the source tree can be found +[here](https://ci.chromium.org/p/pdfium/g/main/console). + +## `infra/config` Branch + +LUCI project configurations for PDFium's CI and CQ builders live on the +[`infra/config` branch](https://pdfium.googlesource.com/pdfium/+/refs/heads/infra/config), +which is separate from the `main` branch. Code reviews for this branch are on +Gerrit under +[`project:pdfium branch:infra/config`](https://pdfium-review.googlesource.com/q/project:pdfium+branch:infra/config). + +To check out the `infra/config` branch locally in an existing PDFium repository: + +```shell +git fetch origin refs/heads/infra/config +git checkout -b my_branch origin/infra/config +``` + +Note that the `infra/config` branch does not have a `DEPS` file, so there is no +need to run `gclient sync`. + +When uploading CLs from the `infra/config` branch, be sure to run +`git branch --set-upstream-to origin/infra/config`. + +### Builder Configurations + +`main.star` defines the builders, dimensions, properties, console views, and CQ +verifiers using Starlark. Running `main.star` regenerates the LUCI configuration +files under `generated/`: + +```shell +./main.star +``` + +Do not edit the files in `generated/` directly. Always modify `main.star` and +regenerate the configuration files. + +## Build Recipes + +The build recipes that control how bots check out, configure, compile, and test +PDFium live in the +[`chromium/tools/build`](https://chromium.googlesource.com/chromium/tools/build) +repository. + +The PDFium recipe is located at +[`recipes/recipes/pdfium.py`](https://chromium.googlesource.com/chromium/tools/build/+/refs/heads/main/recipes/recipes/pdfium.py). +After modifying the recipe, run `recipes/recipes.py test train` to run the +tests and update the expectation files under `recipes/recipes/pdfium.expected/`: + +```shell +./recipes/recipes.py test train +``` + +### Order of changes + +When a task involves updates to recipes and builder configurations, usually one +should update the recipes first with a default that is effectively a no-op, then +update the builder config to active the non-default behavior. e.g. +[recipe change](https://crrev.com/c/7588614) and +[builder change](https://pdfium-review.googlesource.com/c/149210). diff --git a/fpdfsdk/epdf_view_embeddertest.cpp b/fpdfsdk/epdf_view_embeddertest.cpp index d3e76b1794..aa1e8fc3af 100644 --- a/fpdfsdk/epdf_view_embeddertest.cpp +++ b/fpdfsdk/epdf_view_embeddertest.cpp @@ -628,8 +628,6 @@ TEST_F(EPDFViewEmbedderTest, SpecializedResampleLoopsMatchGeneralOnes) { } TEST_F(EPDFViewEmbedderTest, EPDFDocSetPageRotationByObjectNumber) { - constexpr char kRotatedPng[] = "rectangles_rotated"; - ASSERT_TRUE(OpenDocument("rectangles.pdf")); const unsigned int objnum = EPDFDoc_GetPageObjectNumberByIndex(document(), 0); @@ -653,8 +651,8 @@ TEST_F(EPDFViewEmbedderTest, EPDFDocSetPageRotationByObjectNumber) { EXPECT_EQ(1, FPDFPage_GetRotation(page.get())); EXPECT_EQ(300, static_cast(FPDF_GetPageWidth(page.get()))); EXPECT_EQ(200, static_cast(FPDF_GetPageHeight(page.get()))); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRotatedPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_rotated"); } ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp index 34f2b65dd0..e5a7dec345 100644 --- a/fpdfsdk/fpdf_annot_embeddertest.cpp +++ b/fpdfsdk/fpdf_annot_embeddertest.cpp @@ -38,7 +38,6 @@ #include "testing/utils/compare_coordinates.h" #include "testing/utils/hash.h" -using pdfium::kAnnotationStampWithApPng; namespace { @@ -581,11 +580,10 @@ TEST_F(FPDFAnnotEmbedderTest, ExtractInkMultiple) { EXPECT_EQ(475.336121f, rect.right); EXPECT_EQ(681.535034f, rect.top); } - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), "annotation_ink_multiple", - kFuzzyDiffOptions); - } + // EmbedPDF(own-appearance-generator): our ink appearance strokes with round + // caps and joins and fits its box to the stroke; the dump is ours. + CompareDisplayList(RenderLoadedPageWithFlagsToDisplayList(page, FPDF_ANNOT), + "annotation_ink_multiple"); UnloadPageNoEvents(page); } @@ -799,9 +797,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndSaveUnderlineAnnotation) { ASSERT_TRUE(saved_doc); ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - VerifySavedRenderingWithExpectationSuffix( + VerifySavedRenderingWithDisplayList( saved_page.get(), "annotation_highlight_long_content_added_underline", - kFuzzyDiffOptions); + FPDF_ANNOT); // Check that the saved document has 2 annotations on the first page EXPECT_EQ(2, FPDFPage_GetAnnotCount(saved_page.get())); @@ -912,13 +910,9 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) { EXPECT_EQ(4, FPDFPage_GetAnnotCount(page.get())); // Check that the original file renders correctly. - static constexpr char kAnnotationHighlightSquareWithApPng[] = - "annotation_highlight_square_with_ap"; - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), kAnnotationHighlightSquareWithApPng, kFuzzyDiffOptions); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_highlight_square_with_ap"); FS_RECTF rect; FS_RECTF new_rect; @@ -955,12 +949,9 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) { EXPECT_EQ(quadpoints.y4, new_quadpoints.y4); // Check that updating quadpoints does not change the annotation's position. - { - ScopedFPDFBitmap bitmap = - RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), kAnnotationHighlightSquareWithApPng, kFuzzyDiffOptions); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_highlight_square_with_ap"); // Verify its annotation rectangle. ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect)); @@ -978,12 +969,9 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) { } // Check that updating the rectangle changes the annotation's position. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), "annotation_highlight_square_with_ap_modified_highlight", - kFuzzyDiffOptions); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_highlight_square_with_ap_modified_highlight"); { // Retrieve the square annotation which has its AP stream already defined. @@ -1001,10 +989,9 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) { // Check that updating the rectangle changes the square annotation's // position. - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), "annotation_highlight_square_with_ap_modified_square", - kFuzzyDiffOptions); + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_highlight_square_with_ap_modified_square"); } } @@ -1108,10 +1095,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) { EXPECT_EQ(2, FPDFPage_GetAnnotCount(page.get())); // Check that the page renders correctly. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), kAnnotationStampWithApPng); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap"); { // Retrieve the stamp annotation which has its AP stream already defined. @@ -1132,12 +1118,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) { EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), path)); // Check that the page with the modified annotation renders correctly. - { - ScopedFPDFBitmap bitmap = - RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), "annotation_stamp_with_ap_modified_path"); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap_modified_path"); // Add a second path object to the same annotation. FPDF_PAGEOBJECT dot = FPDFPageObj_CreateNewPath(7, 84); @@ -1153,12 +1136,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) { ASSERT_EQ(32, FPDFPage_CountObjects(page.get())); // Check that the page with an annotation with two paths renders correctly. - { - ScopedFPDFBitmap bitmap = - RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), - "annotation_stamp_with_ap_two_paths"); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap_two_paths"); // Delete the newly added path object. EXPECT_TRUE(FPDFAnnot_RemoveObject(annot.get(), 1)); @@ -1167,11 +1147,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) { } // Check that the page renders the same as before. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), "annotation_stamp_with_ap_modified_path"); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap_modified_path"); FS_RECTF rect; @@ -1214,8 +1192,8 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) { ASSERT_TRUE(saved_doc); ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - VerifySavedRenderingWithExpectationSuffix( - saved_page.get(), "annotation_stamp_with_ap_new_annot"); + VerifySavedRenderingWithDisplayList( + saved_page.get(), "annotation_stamp_with_ap_new_annot", FPDF_ANNOT); // Check that the document has a correct count of annotations and objects. EXPECT_EQ(3, FPDFPage_GetAnnotCount(saved_page.get())); @@ -1298,10 +1276,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) { EXPECT_EQ(2, FPDFPage_GetAnnotCount(page.get())); // Check that the page renders correctly. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), kAnnotationStampWithApPng); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap"); static constexpr int kBitmapSize = 200; FPDF_BITMAP image_bitmap; @@ -1335,11 +1312,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) { } // Check that the page renders correctly with the new image object. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), "annotation_stamp_with_ap_new_image", kFuzzyDiffOptions); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap_new_image"); { // Retrieve the newly added stamp annotation and its image object. @@ -1363,8 +1338,8 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) { FPDFBitmap_Destroy(image_bitmap); // Test that the saved document renders the modified image object correctly. - VerifySavedDocumentWithExpectationSuffix( - "annotation_stamp_with_ap_modified_image"); + VerifySavedDocumentWithDisplayList("annotation_stamp_with_ap_modified_image", + FPDF_ANNOT); } TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) { @@ -1375,10 +1350,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) { EXPECT_EQ(2, FPDFPage_GetAnnotCount(page.get())); // Check that the page renders correctly. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), kAnnotationStampWithApPng); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap"); { // Create a stamp annotation and set its annotation rectangle. @@ -1405,11 +1379,9 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) { } // Check that the page renders correctly with the new text object. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix( - bitmap.get(), "annotation_stamp_with_ap_new_text", kFuzzyDiffOptions); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap_new_text"); { // Retrieve the newly added stamp annotation and its text object. @@ -1426,19 +1398,15 @@ TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) { } // Check that the page renders correctly with the modified text object. - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), - "annotation_stamp_with_ap_modified_text", - kFuzzyDiffOptions); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap_modified_text"); // Remove the new annotation, and check that the page renders as before. EXPECT_TRUE(FPDFPage_RemoveAnnot(page.get(), 2)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), kAnnotationStampWithApPng); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annotation_stamp_with_ap"); } TEST_F(FPDFAnnotEmbedderTest, GetSetStringValue) { @@ -1500,8 +1468,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetSetStringValue) { ASSERT_TRUE(saved_doc); ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - VerifySavedRenderingWithExpectationSuffix(saved_page.get(), - kAnnotationStampWithApPng); + VerifySavedRenderingWithDisplayList(saved_page.get(), + "annotation_stamp_with_ap", FPDF_ANNOT); ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(saved_page.get(), 0)); @@ -2598,16 +2566,16 @@ TEST_F(FPDFAnnotEmbedderTest, GetFontSizeNegative) { } TEST_F(FPDFAnnotEmbedderTest, SetFontColor) { + // EmbedPDF(own-appearance-generator): our free text appearance insets its + // first line differently; the freetext_annotation_without_da dumps are ours. ASSERT_TRUE(OpenDocument("freetext_annotation_without_da.pdf")); ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - static constexpr char kFreetextAnnotationWithoutDaModifiedPng[] = - "freetext_annotation_without_da_modified"; { - ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), - "freetext_annotation_without_da"); + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "freetext_annotation_without_da"); // Obtain the only annotation and set its text color. ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page.get(), 0)); @@ -2629,9 +2597,9 @@ TEST_F(FPDFAnnotEmbedderTest, SetFontColor) { EXPECT_EQ(120u, g); EXPECT_EQ(180u, b); - bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), - kFreetextAnnotationWithoutDaModifiedPng); + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "freetext_annotation_without_da_modified"); } EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); @@ -2640,8 +2608,8 @@ TEST_F(FPDFAnnotEmbedderTest, SetFontColor) { ASSERT_TRUE(saved_doc); ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - VerifySavedRenderingWithExpectationSuffix( - saved_page.get(), kFreetextAnnotationWithoutDaModifiedPng); + VerifySavedRenderingWithDisplayList( + saved_page.get(), "freetext_annotation_without_da_modified", FPDF_ANNOT); } TEST_F(FPDFAnnotEmbedderTest, SetFontColorNegative) { @@ -3574,27 +3542,22 @@ TEST_F(FPDFAnnotEmbedderTest, AnnotationBorderRendering) { ASSERT_TRUE(page); EXPECT_EQ(3, FPDFPage_GetAnnotCount(page.get())); - static constexpr char kAnnotsPage2ModifiedPng[] = "annots_page2_modified"; { ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page.get(), 2)); ASSERT_TRUE(annot); EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot.get())); - { - ScopedFPDFBitmap bitmap = - RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), "annots_page2"); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annots_page2"); EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f, /*vertical_radius=*/3.5f, /*border_width=*/4.0f)); - { - ScopedFPDFBitmap bitmap = - RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), kAnnotsPage2ModifiedPng); - } + CompareDisplayList( + RenderLoadedPageWithFlagsToDisplayList(page.get(), FPDF_ANNOT), + "annots_page2_modified"); } // Save the document and close the page. @@ -3605,8 +3568,8 @@ TEST_F(FPDFAnnotEmbedderTest, AnnotationBorderRendering) { ASSERT_TRUE(saved_doc); ScopedSavedPage saved_page = LoadScopedSavedPage(1); ASSERT_TRUE(saved_page); - VerifySavedRenderingWithExpectationSuffix(saved_page.get(), - kAnnotsPage2ModifiedPng); + VerifySavedRenderingWithDisplayList(saved_page.get(), + "annots_page2_modified", FPDF_ANNOT); } } diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp index 7cd74389a5..22048cff59 100644 --- a/fpdfsdk/fpdf_edit_embeddertest.cpp +++ b/fpdfsdk/fpdf_edit_embeddertest.cpp @@ -42,6 +42,7 @@ #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/gmock/include/gmock/gmock-matchers.h" #include "testing/gtest/include/gtest/gtest.h" @@ -767,11 +768,8 @@ TEST_F(FPDFEditEmbedderTest, SetText) { // now displayed. ASSERT_EQ(2, FPDFPage_CountObjects(page.get())); - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - "hello_world_with_changed_text"); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), + "hello_world_with_changed_text"); // Now save the result. EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); @@ -783,11 +781,8 @@ TEST_F(FPDFEditEmbedderTest, SetText) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); EXPECT_EQ(2, FPDFPage_CountObjects(saved_page.get())); - { - ScopedFPDFBitmap page_bitmap = RenderPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - "hello_world_with_changed_text"); - } + CompareDisplayList(RenderPageToDisplayList(saved_page.get()), + "hello_world_with_changed_text"); } TEST_F(FPDFEditEmbedderTest, SetTextBadParams) { @@ -1107,11 +1102,8 @@ TEST_F(FPDFEditEmbedderTest, SetTextKeepClippingPath) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - // When opened before any editing and saving, the clipping path is rendered. - ScopedFPDFBitmap original_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(original_bitmap.get(), "bug_1558"); - } + // When opened before any editing and saving, the clipping path is rendered. + CompareDisplayList(RenderPageToDisplayList(page.get()), "bug_1558"); // "Change" the text in the objects to their current values to force them to // regenerate when saving. @@ -1134,11 +1126,8 @@ TEST_F(FPDFEditEmbedderTest, SetTextKeepClippingPath) { } } - { - // After editing but before saving, the clipping path is retained. - ScopedFPDFBitmap edited_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(edited_bitmap.get(), "bug_1558"); - } + // After editing but before saving, the clipping path is retained. + CompareDisplayList(RenderPageToDisplayList(page.get()), "bug_1558"); // Save the file. EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); @@ -1150,10 +1139,7 @@ TEST_F(FPDFEditEmbedderTest, SetTextKeepClippingPath) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - { - ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(saved_bitmap.get(), "bug_1558"); - } + CompareDisplayList(RenderPageToDisplayList(saved_page.get()), "bug_1558"); } TEST_F(FPDFEditEmbedderTest, Bug1574) { @@ -1162,11 +1148,8 @@ TEST_F(FPDFEditEmbedderTest, Bug1574) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - // When opened before any editing and saving, the text object is rendered. - ScopedFPDFBitmap original_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(original_bitmap.get(), "bug_1574"); - } + // When opened before any editing and saving, the text object is rendered. + CompareDisplayList(RenderPageToDisplayList(page.get()), "bug_1574"); // "Change" the text in the objects to their current values to force them to // regenerate when saving. @@ -1197,19 +1180,13 @@ TEST_F(FPDFEditEmbedderTest, Bug1574) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - { - ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(saved_bitmap.get(), "bug_1574"); - } + CompareDisplayList(RenderPageToDisplayList(saved_page.get()), "bug_1574"); } TEST_F(FPDFEditEmbedderTest, Bug1893) { ASSERT_TRUE(OpenDocument("bug_1893.pdf")); ScopedPage page = LoadScopedPage(0); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "bug_1893"); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "bug_1893"); EXPECT_EQ(3, FPDFPage_CountObjects(page.get())); @@ -1219,9 +1196,8 @@ TEST_F(FPDFEditEmbedderTest, Bug1893) { ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), object.get())); ASSERT_TRUE(FPDFPage_GenerateContent(page.get())); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), - "bug_1893_removed_underline"); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "bug_1893_removed_underline"); } ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); @@ -1230,10 +1206,9 @@ TEST_F(FPDFEditEmbedderTest, Bug1893) { ScopedSavedDoc saved_document = OpenScopedSavedDocument(); ASSERT_TRUE(saved_document); ScopedSavedPage saved_page = LoadScopedSavedPage(0); - ScopedFPDFBitmap bitmap = - RenderSavedPageWithFlags(saved_page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(bitmap.get(), - "bug_1893_removed_underline"); + CompareDisplayList( + RenderSavedPageWithFlagsToDisplayList(saved_page.get(), FPDF_ANNOT), + "bug_1893_removed_underline"); } } @@ -1571,11 +1546,8 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarkedObjectsPrime) { ASSERT_TRUE(page); // Show what the original file looks like. - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - "text_in_page_marked"); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), + "text_in_page_marked"); static constexpr int expected_object_count = 19; CheckMarkCounts(page.get(), 1, expected_object_count, 8, 4, 9, 1); @@ -1608,14 +1580,9 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarkedObjectsPrime) { EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object)); } - static constexpr char kTextInPageMarkedNonPrimesPng[] = - "text_in_page_marked_non_primes"; EXPECT_EQ(11, FPDFPage_CountObjects(page.get())); - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - kTextInPageMarkedNonPrimesPng); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), + "text_in_page_marked_non_primes"); // Save the file. EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); @@ -1627,12 +1594,8 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarkedObjectsPrime) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); EXPECT_EQ(11, FPDFPage_CountObjects(saved_page.get())); - - { - ScopedFPDFBitmap page_bitmap = RenderPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - kTextInPageMarkedNonPrimesPng); - } + CompareDisplayList(RenderPageToDisplayList(saved_page.get()), + "text_in_page_marked_non_primes"); } TEST_F(FPDFEditEmbedderTest, RemoveMarks) { @@ -2702,8 +2665,8 @@ TEST_F(FPDFEditEmbedderTest, PathOnTopOfText) { EXPECT_TRUE(FPDFPage_InsertObject(page.get(), black_path)); // Render and check the result. - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "hello_world_added_paths"); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "hello_world_added_paths"); } TEST_F(FPDFEditEmbedderTest, EditOverExistingContent) { @@ -2991,8 +2954,12 @@ TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) { } TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) { + auto* env = EmbedderTestEnvironment::GetInstance(); static constexpr char kTextRenderModeStrokeModePng[] = "text_render_mode_stroke_mode"; + static constexpr DiffOptions kTextRenderModeDiffOptions = { + .max_pixel_per_channel_delta = 30, + }; { ASSERT_TRUE(OpenDocument("text_render_mode.pdf")); ScopedPage page = LoadScopedPage(0); @@ -3002,7 +2969,11 @@ TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) { // Check the bitmap { ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), "text_render_mode"); + CompareBitmapWithExpectationSuffix( + page_bitmap.get(), "text_render_mode", + env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS + ? kTextRenderModeDiffOptions + : kExactDiffOptions); } // Cannot set on a null object. @@ -3028,8 +2999,11 @@ TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) { // Check that bitmap displays changed content { ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - kTextRenderModeStrokeModePng); + CompareBitmapWithExpectationSuffix( + page_bitmap.get(), kTextRenderModeStrokeModePng, + env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS + ? kTextRenderModeDiffOptions + : kExactDiffOptions); } // Save a copy. @@ -3051,8 +3025,11 @@ TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) { FPDFTextObj_GetTextRenderMode(page_object)); ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), - kTextRenderModeStrokeModePng); + CompareBitmapWithExpectationSuffix( + bitmap.get(), kTextRenderModeStrokeModePng, + env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS + ? kTextRenderModeDiffOptions + : kExactDiffOptions); } } @@ -3355,10 +3332,7 @@ TEST_F(FPDFEditEmbedderTest, ModifyFormObject) { ASSERT_TRUE(page); ASSERT_EQ(1, FPDFPage_CountObjects(page.get())); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "form_object"); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "form_object"); FPDF_PAGEOBJECT form = FPDFPage_GetObject(page.get(), 0); ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form)); @@ -3366,14 +3340,11 @@ TEST_F(FPDFEditEmbedderTest, ModifyFormObject) { FPDFPageObj_Transform(form, 0.5, 0, 0, 0.5, 0, 0); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); - static constexpr char kFormObjectScaledPng[] = "form_object_scaled"; - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kFormObjectScaledPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_scaled"); EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); - VerifySavedDocumentWithExpectationSuffix(kFormObjectScaledPng); + VerifySavedDocumentWithDisplayList("form_object_scaled"); } // Tests adding text from standard font using FPDFText_LoadStandardFont. @@ -4049,10 +4020,7 @@ TEST_F(FPDFEditEmbedderTest, AddMarkCompressedStream) { ASSERT_TRUE(page); // Render and check there are no marks. - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "hello_world"); CheckMarkCounts(page.get(), 0, 2, 0, 0, 0, 0); // Add to the first page object a "Bounds" mark with "Position": "First". @@ -4063,10 +4031,7 @@ TEST_F(FPDFEditEmbedderTest, AddMarkCompressedStream) { "Position", "First")); // Render and check there is 1 mark. - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "hello_world"); CheckMarkCounts(page.get(), 0, 2, 0, 0, 0, 1); // Save the file. @@ -4079,10 +4044,7 @@ TEST_F(FPDFEditEmbedderTest, AddMarkCompressedStream) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - { - ScopedFPDFBitmap page_bitmap = RenderPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(saved_page.get()), "hello_world"); CheckMarkCounts(saved_page.get(), 0, 2, 0, 0, 0, 1); } @@ -4113,10 +4075,7 @@ TEST_F(FPDFEditEmbedderTest, AddExistingMarkCompressedStream) { ASSERT_TRUE(page); // Render and check there are no marks. - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "hello_world"); CheckMarkCounts(page.get(), 0, 2, 0, 0, 0, 0); // Add a mark to the first page object @@ -4127,10 +4086,7 @@ TEST_F(FPDFEditEmbedderTest, AddExistingMarkCompressedStream) { "Test", "Hello")); // Render and check there is 1 mark. - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "hello_world"); CheckMarkCounts(page.get(), 0, 2, 1, 0, 0, 0); // Add the same bounds mark to the second object. @@ -4138,10 +4094,7 @@ TEST_F(FPDFEditEmbedderTest, AddExistingMarkCompressedStream) { EXPECT_TRUE(FPDFPageObj_AddExistingMark(page_object2, mark)); // Render and check there are 2 marks. - { - ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "hello_world"); CheckMarkCounts(page.get(), 0, 2, 2, 0, 0, 0); // Save the file. @@ -4154,10 +4107,7 @@ TEST_F(FPDFEditEmbedderTest, AddExistingMarkCompressedStream) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - { - ScopedFPDFBitmap page_bitmap = RenderPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderPageToDisplayList(saved_page.get()), "hello_world"); CheckMarkCounts(saved_page.get(), 0, 2, 2, 0, 0, 0); } @@ -5282,6 +5232,8 @@ TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForRotatedText) { } TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForColorText) { + auto* env = EmbedderTestEnvironment::GetInstance(); + ASSERT_TRUE(OpenDocument("text_color.pdf")); ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); @@ -5292,7 +5244,14 @@ TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForColorText) { ScopedFPDFBitmap bitmap( FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 7.3f)); ASSERT_TRUE(bitmap); - CompareBitmapWithExpectationSuffix(bitmap.get(), "text_color_text_object"); + static constexpr DiffOptions kColorTextDiffOptions = { + .max_pixel_per_channel_delta = 16, + }; + CompareBitmapWithExpectationSuffix( + bitmap.get(), "text_color_text_object", + env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS + ? kColorTextDiffOptions + : kExactDiffOptions); } TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForNewlyCreatedText) { @@ -5560,10 +5519,8 @@ TEST_F(FPDFEditEmbedderTest, GetAndSetMatrixForFormWithText) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_text"); FPDF_PAGEOBJECT form = FPDFPage_GetObject(page.get(), 0); ASSERT_TRUE(form); @@ -5575,10 +5532,8 @@ TEST_F(FPDFEditEmbedderTest, GetAndSetMatrixForFormWithText) { CompareFS_MATRIX({2.0f, 0.0f, 0.0f, -1.0f, 0.0f, 200.0f}, matrix); ASSERT_TRUE(FPDFPageObj_SetMatrix(form, &matrix)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_text"); FPDF_PAGEOBJECT text = FPDFFormObj_GetObject(form, 0); ASSERT_TRUE(text); @@ -5588,20 +5543,16 @@ TEST_F(FPDFEditEmbedderTest, GetAndSetMatrixForFormWithText) { CompareFS_MATRIX({0.5f, 0.0f, 0.0f, -1.0f, 10.0f, 150.0f}, matrix); ASSERT_TRUE(FPDFPageObj_SetMatrix(text, &matrix)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_text"); ASSERT_TRUE(FPDFPage_GenerateContent(page.get())); ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kHelloWorldPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_text"); - VerifySavedDocumentWithExpectationSuffix(kHelloWorldPng); + VerifySavedDocumentWithDisplayList("form_object_with_text"); } TEST_F(FPDFEditEmbedderTest, PageObjTransformFWithBadParameters) { @@ -5831,10 +5782,7 @@ TEST_F(FPDFEditEmbedderTest, Bug461845674) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - ScopedFPDFBitmap bitmap = RenderPage(page.get()); - CompareBitmap(bitmap.get(), kBlankPage200x200Png); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "blank_page_200x200"); ScopedFPDFPageObject text_object( FPDFPageObj_NewTextObj(document(), "Arial", 50.0f)); @@ -5846,14 +5794,10 @@ TEST_F(FPDFEditEmbedderTest, Bug461845674) { EXPECT_TRUE(FPDFPage_InsertObject(page.get(), text_object.release())); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); - static constexpr char kExpected[] = "bug_461845674"; - { - ScopedFPDFBitmap bitmap = RenderPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kExpected); - } + CompareDisplayList(RenderPageToDisplayList(page.get()), "bug_461845674"); EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); - // TODO(crbug.com/461845674): The saved doc should not render blank. The - // rendering should match `kExpected`. - VerifySavedDocument(kBlankPage200x200Png); + // TODO(crbug.com/461845674): The saved doc should have the same display list + // as before saving. + VerifySavedDocumentWithDisplayList("bug_461845674_saved"); } diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp index c4cf9088ef..ca5908dd58 100644 --- a/fpdfsdk/fpdf_editpage_embeddertest.cpp +++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp @@ -5,11 +5,11 @@ #include "public/fpdf_edit.h" #include +#include #include "core/fxcrt/fx_system.h" #include "core/fxge/cfx_renderdevice.h" #include "testing/embedder_test.h" -#include "testing/embedder_test_constants.h" #include "testing/gmock/include/gmock/gmock.h" using ::testing::Each; @@ -20,8 +20,6 @@ using ::testing::Gt; class FPDFEditPageEmbedderTest : public EmbedderTest {}; TEST_F(FPDFEditPageEmbedderTest, Rotation) { - constexpr char kRotatedPng[] = "rectangles_rotated"; - { ASSERT_TRUE(OpenDocument("rectangles.pdf")); ScopedPage page = LoadScopedPage(0); @@ -34,8 +32,8 @@ TEST_F(FPDFEditPageEmbedderTest, Rotation) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } FPDFPage_SetRotation(page.get(), 1); @@ -49,8 +47,8 @@ TEST_F(FPDFEditPageEmbedderTest, Rotation) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(300, page_width); EXPECT_EQ(200, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRotatedPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_rotated"); } } @@ -70,8 +68,8 @@ TEST_F(FPDFEditPageEmbedderTest, Rotation) { static_cast(FPDF_GetPageHeight(saved_page.get())); EXPECT_EQ(300, page_width); EXPECT_EQ(200, page_height); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRotatedPng); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles_rotated"); } } @@ -488,9 +486,6 @@ TEST_F(FPDFEditPageEmbedderTest, VerifyDashArraySaved) { } TEST_F(FPDFEditPageEmbedderTest, PageObjectActiveState) { - constexpr char kOneRectangleInactivePng[] = - "rectangles_one_rectangle_inactive"; - ASSERT_TRUE(OpenDocument("rectangles.pdf")); ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); @@ -500,8 +495,7 @@ TEST_F(FPDFEditPageEmbedderTest, PageObjectActiveState) { { // Render the page as is. - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "rectangles"); } { @@ -514,9 +508,8 @@ TEST_F(FPDFEditPageEmbedderTest, PageObjectActiveState) { // Note that all page objects for the rectangles are present in the copy. EXPECT_EQ(8, FPDFPage_CountObjects(saved_page.get())); - - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles"); } // Mark one of the page objects as inactive. It is still present in the page. @@ -551,8 +544,8 @@ TEST_F(FPDFEditPageEmbedderTest, PageObjectActiveState) { EXPECT_EQ(7, FPDFPage_CountObjects(saved_page.get())); // The absence of the inactive page object affects the rendered result. - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kOneRectangleInactivePng); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles_one_rectangle_inactive"); } // Negative testing. @@ -561,16 +554,14 @@ TEST_F(FPDFEditPageEmbedderTest, PageObjectActiveState) { } TEST_F(FPDFEditPageEmbedderTest, Bug378120423) { - constexpr char kBug378120423Png[] = "bug_378120423"; - ASSERT_TRUE(OpenDocument("bug_378120423.pdf")); ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); { // Render the page as is. - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmap(bitmap.get(), kBug378120423Png); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "bug_378120423"); EXPECT_EQ(1, FPDFPage_CountObjects(page.get())); } @@ -579,8 +570,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378120423) { ASSERT_TRUE(FPDFPageObj_SetIsActive(page_obj, false)); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmap(bitmap.get(), pdfium::kBlankPage200x200Png); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "blank_page_200x200"); // `page_obj` can still be found. It is just deactivated. EXPECT_EQ(1, FPDFPage_CountObjects(page.get())); } @@ -593,8 +584,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378120423) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmap(bitmap.get(), pdfium::kBlankPage200x200Png); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "blank_page_200x200"); // `page_obj` did not get written out to the saved PDF. EXPECT_EQ(0, FPDFPage_CountObjects(saved_page.get())); } @@ -603,8 +594,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378120423) { ASSERT_TRUE(FPDFPageObj_SetIsActive(page_obj, true)); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmap(bitmap.get(), kBug378120423Png); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "bug_378120423"); EXPECT_EQ(1, FPDFPage_CountObjects(page.get())); } @@ -616,8 +607,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378120423) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmap(bitmap.get(), kBug378120423Png); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "bug_378120423"); EXPECT_EQ(1, FPDFPage_CountObjects(saved_page.get())); } } @@ -629,8 +620,7 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { static constexpr int kOriginalObjectCount = 8; { // Sanity check rectangles.pdf before modifying it. - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "rectangles"); EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(page.get())); } @@ -650,10 +640,9 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); // Render `page` with the new path. - constexpr char kNewPathPng[] = "bug_378464305"; { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kNewPathPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "bug_378464305"); EXPECT_EQ(kObjectCountWithNewPath, FPDFPage_CountObjects(page.get())); } { @@ -664,8 +653,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kNewPathPng); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "bug_378464305_saved"); EXPECT_EQ(kObjectCountWithNewPath, FPDFPage_CountObjects(saved_page.get())); } @@ -673,8 +662,7 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { ASSERT_TRUE(FPDFPageObj_SetIsActive(path, false)); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "rectangles"); // `path` can still be found. It is just deactivated. EXPECT_EQ(kObjectCountWithNewPath, FPDFPage_CountObjects(page.get())); } @@ -687,8 +675,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles"); // `path` did not get written out to the saved PDF. EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page.get())); } @@ -697,8 +685,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { ASSERT_TRUE(FPDFPageObj_SetIsActive(path, true)); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kNewPathPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "bug_378464305"); EXPECT_EQ(kObjectCountWithNewPath, FPDFPage_CountObjects(page.get())); } @@ -710,8 +698,8 @@ TEST_F(FPDFEditPageEmbedderTest, Bug378464305) { ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kNewPathPng); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "bug_378464305_saved"); EXPECT_EQ(kObjectCountWithNewPath, FPDFPage_CountObjects(saved_page.get())); } } diff --git a/fpdfsdk/fpdf_editpath_embeddertest.cpp b/fpdfsdk/fpdf_editpath_embeddertest.cpp index dbddb6ebb0..e8155fd99d 100644 --- a/fpdfsdk/fpdf_editpath_embeddertest.cpp +++ b/fpdfsdk/fpdf_editpath_embeddertest.cpp @@ -7,15 +7,12 @@ #include "core/fxge/cfx_renderdevice.h" #include "public/fpdf_edit.h" #include "testing/embedder_test.h" -#include "testing/embedder_test_constants.h" #include "testing/gtest/include/gtest/gtest.h" using FPDFEditPathEmbedderTest = EmbedderTest; namespace { -constexpr char kRectanglesAndTrianglePng[] = "rectangles_and_triangle"; - ScopedFPDFPageObject CreateBlackTriangle() { ScopedFPDFPageObject path(FPDFPageObj_CreateNewPath(100, 50)); EXPECT_TRUE(FPDFPageObj_SetFillColor(path.get(), 0, 0, 0, 255)); @@ -85,10 +82,8 @@ TEST_F(FPDFEditPathEmbedderTest, GetAndSetMatrixForPath) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_double_flipped"); FPDF_PAGEOBJECT path = FPDFPage_GetObject(page.get(), 0); ASSERT_TRUE(path); @@ -104,20 +99,15 @@ TEST_F(FPDFEditPathEmbedderTest, GetAndSetMatrixForPath) { EXPECT_FLOAT_EQ(300.0f, matrix.f); ASSERT_TRUE(FPDFPageObj_SetMatrix(path, &matrix)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_double_flipped"); ASSERT_TRUE(FPDFPage_GenerateContent(page.get())); ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } - - VerifySavedDocumentWithExpectationSuffix(pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_double_flipped"); + VerifySavedDocumentWithDisplayList("rectangles_double_flipped"); } TEST_F(FPDFEditPathEmbedderTest, GetAndSetMatrixForFormWithPath) { @@ -125,10 +115,8 @@ TEST_F(FPDFEditPathEmbedderTest, GetAndSetMatrixForFormWithPath) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_path"); FPDF_PAGEOBJECT form = FPDFPage_GetObject(page.get(), 0); ASSERT_TRUE(form); @@ -144,10 +132,8 @@ TEST_F(FPDFEditPathEmbedderTest, GetAndSetMatrixForFormWithPath) { EXPECT_FLOAT_EQ(300.0f, matrix.f); ASSERT_TRUE(FPDFPageObj_SetMatrix(form, &matrix)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_path"); FPDF_PAGEOBJECT path = FPDFFormObj_GetObject(form, 0); ASSERT_TRUE(path); @@ -162,20 +148,15 @@ TEST_F(FPDFEditPathEmbedderTest, GetAndSetMatrixForFormWithPath) { EXPECT_FLOAT_EQ(300.0f, matrix.f); ASSERT_TRUE(FPDFPageObj_SetMatrix(path, &matrix)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_path"); ASSERT_TRUE(FPDFPage_GenerateContent(page.get())); ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } - - VerifySavedDocumentWithExpectationSuffix(pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "form_object_with_path"); + VerifySavedDocumentWithDisplayList("form_object_with_path"); } TEST_F(FPDFEditPathEmbedderTest, AddPathToRectangles) { @@ -183,29 +164,20 @@ TEST_F(FPDFEditPathEmbedderTest, AddPathToRectangles) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "rectangles"); ScopedFPDFPageObject path = CreateBlackTriangle(); ASSERT_TRUE(path); FPDFPage_InsertObject(page.get(), path.release()); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRectanglesAndTrianglePng); - } - + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_and_triangle"); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); - EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_and_triangle"); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRectanglesAndTrianglePng); - } - - VerifySavedDocumentWithExpectationSuffix(kRectanglesAndTrianglePng); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + VerifySavedDocumentWithDisplayList("rectangles_and_triangle_saved"); } TEST_F(FPDFEditPathEmbedderTest, AddPathToRectanglesWithLeakyCTM) { @@ -213,27 +185,18 @@ TEST_F(FPDFEditPathEmbedderTest, AddPathToRectanglesWithLeakyCTM) { ScopedPage page = LoadScopedPage(0); ASSERT_TRUE(page); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); - } + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), "rectangles"); ScopedFPDFPageObject path = CreateBlackTriangle(); ASSERT_TRUE(path); FPDFPage_InsertObject(page.get(), path.release()); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRectanglesAndTrianglePng); - } - + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_and_triangle"); EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); - EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_and_triangle"); - { - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), kRectanglesAndTrianglePng); - } - - VerifySavedDocumentWithExpectationSuffix(kRectanglesAndTrianglePng); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + VerifySavedDocumentWithDisplayList("rectangles_and_triangle_saved"); } diff --git a/fpdfsdk/fpdf_save_embeddertest.cpp b/fpdfsdk/fpdf_save_embeddertest.cpp index 3d047a8f17..cfa5fb6bfa 100644 --- a/fpdfsdk/fpdf_save_embeddertest.cpp +++ b/fpdfsdk/fpdf_save_embeddertest.cpp @@ -19,7 +19,6 @@ #include "public/fpdf_text.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" @@ -210,8 +209,8 @@ TEST_F(FPDFSaveEmbedderTest, Bug1409) { ASSERT_TRUE(saved_document); ScopedSavedPage saved_page = LoadScopedSavedPage(0); ASSERT_TRUE(saved_page); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmap(bitmap.get(), pdfium::kBlankPage612By792Png); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "blank_page_612x792"); EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); EXPECT_THAT(GetString(), HasSubstr("/Root ")); @@ -326,6 +325,17 @@ TEST_F(FPDFSaveEmbedderTest, IncrementalSaveWithModifications) { EXPECT_EQ(1, count_text_objects(saved_page.get())); } +TEST_F(FPDFSaveEmbedderTest, Pdf2FileVersion) { + ASSERT_TRUE(OpenDocument("pdf20_minimal.pdf")); + int version = 0; + ASSERT_TRUE(FPDF_GetFileVersion(document(), &version)); + ASSERT_EQ(version, 20); + + ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, FPDF_NO_INCREMENTAL)); + + EXPECT_THAT(GetString(), StartsWith("%PDF-2.0\r\n")); +} + class FPDFSaveWithFontSubsetEmbedderTest : public FPDFSaveEmbedderTest { public: static constexpr std::array kExpectedSingleTextAdded = { diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp index 9adbfc7fae..bf901f8995 100644 --- a/fpdfsdk/fpdf_text_embeddertest.cpp +++ b/fpdfsdk/fpdf_text_embeddertest.cpp @@ -2619,3 +2619,49 @@ TEST_F(FPDFTextEmbedderTest, Arabic) { EXPECT_THAT(pdfium::span(buffer).first(), ElementsAreArray(kExpectedText)); } + +TEST_F(FPDFTextEmbedderTest, ArabicActualTextOrdering) { + ASSERT_TRUE(OpenDocument("arabic_actual_text_ordering.pdf")); + + // The 4 pages represent 4 PDF variants containing the Arabic word "بِسْمِ", + // testing combinations of visual vs logical span order and diacritic vs base + // character stream order within each span. All 4 should have the exact same + // extracted text. + // + // Variant 2 (visual order of spans, base character first within span) + // is the only one that extracts correctly as: + // {0x0628, 0x0650, 0x0633, 0x0652, 0x0645, 0x0650, '\0'} + // + // TODO(crbug.com/525093248): Text extraction for the remaining variants + // currently produces differing results with unintended linebreaks/spaces + // and reversed characters within spans. + // EmbedPDF(bug-fix): what the fork extracts. It reverses an /ActualText + // span only after literal right-to-left text and reads marks with their + // base (Acrobat's shaped runs come out right), so variant 2, spans in + // visual order, keeps that order where upstream reverses it. Ordering a + // line's spans by position would fix both; until then this records it. + const std::vector kExpectedPages[] = { + {0x0645, 0x0650, '\r', '\n', 0x0633, 0x0652, 0x0628, 0x0650, '\0'}, + {0x0645, 0x0650, 0x0633, 0x0652, 0x0628, 0x0650, '\0'}, + {0x0633, 0x0652, 0x0628, 0x0650, ' ', 0x0628, 0x0650, '\r', '\n', 0x0645, + 0x0650, '\0'}, + {0x0633, 0x0652, 0x0628, 0x0650, ' ', 0x0628, 0x0650, '\r', '\n', 0x0645, + 0x0650, 0x0633, 0x0652, '\r', '\n', 0x0645, 0x0650, '\0'}, + }; + + int page_index = 0; + for (const std::vector& expected_page : kExpectedPages) { + ScopedPage page = LoadScopedPage(page_index++); + ASSERT_TRUE(page); + + ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get())); + ASSERT_TRUE(text_page); + + std::array buffer = {}; + int num_chars = + FPDFText_GetText(text_page.get(), 0, buffer.size(), buffer.data()); + ASSERT_GE(num_chars, 0); + EXPECT_THAT(pdfium::span(buffer).first(static_cast(num_chars)), + ElementsAreArray(expected_page)); + } +} diff --git a/fpdfsdk/fpdf_transformpage_embeddertest.cpp b/fpdfsdk/fpdf_transformpage_embeddertest.cpp index f9a03ead7a..2be3ea0bf1 100644 --- a/fpdfsdk/fpdf_transformpage_embeddertest.cpp +++ b/fpdfsdk/fpdf_transformpage_embeddertest.cpp @@ -7,10 +7,9 @@ #include "build/build_config.h" #include "core/fxge/cfx_renderdevice.h" #include "testing/embedder_test.h" -#include "testing/embedder_test_constants.h" #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include "testing/scoped_locale.h" +#include "core/fxcrt/scoped_locale.h" #endif class FPDFTransformEmbedderTest : public EmbedderTest {}; @@ -206,8 +205,8 @@ TEST_F(FPDFTransformEmbedderTest, SetCropBox) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } FPDFPage_SetCropBox(page.get(), 10, 20, 100, 150); @@ -228,8 +227,8 @@ TEST_F(FPDFTransformEmbedderTest, SetCropBox) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(90, page_width); EXPECT_EQ(130, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "rectangles_cropped"); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_cropped"); } } @@ -256,8 +255,8 @@ TEST_F(FPDFTransformEmbedderTest, SetCropBox) { static_cast(FPDF_GetPageHeight(saved_page.get())); EXPECT_EQ(90, page_width); EXPECT_EQ(130, page_height); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "rectangles_cropped"); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles_cropped"); } } @@ -277,8 +276,8 @@ TEST_F(FPDFTransformEmbedderTest, SetMediaBox) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } FPDFPage_SetMediaBox(page.get(), 20, 30, 100, 150); @@ -299,8 +298,8 @@ TEST_F(FPDFTransformEmbedderTest, SetMediaBox) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(80, page_width); EXPECT_EQ(120, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "rectangles_media_box"); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles_media_box"); } } @@ -327,8 +326,8 @@ TEST_F(FPDFTransformEmbedderTest, SetMediaBox) { static_cast(FPDF_GetPageHeight(saved_page.get())); EXPECT_EQ(80, page_width); EXPECT_EQ(120, page_height); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "rectangles_media_box"); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles_media_box"); } } @@ -395,8 +394,8 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSave) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } { @@ -412,8 +411,8 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSave) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } } @@ -432,8 +431,8 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSave) { static_cast(FPDF_GetPageHeight(saved_page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "rectangles_shrunk"); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles_shrunk"); } } @@ -452,8 +451,8 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSaveWithLocale) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } { @@ -469,8 +468,8 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSaveWithLocale) { const int page_height = static_cast(FPDF_GetPageHeight(page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), pdfium::kRectanglesPng); + CompareDisplayList(RenderLoadedPageToDisplayList(page.get()), + "rectangles"); } } @@ -489,8 +488,8 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSaveWithLocale) { static_cast(FPDF_GetPageHeight(saved_page.get())); EXPECT_EQ(200, page_width); EXPECT_EQ(300, page_height); - ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page.get()); - CompareBitmapWithExpectationSuffix(bitmap.get(), "rectangles_shrunk"); + CompareDisplayList(RenderSavedPageToDisplayList(saved_page.get()), + "rectangles_shrunk"); } } #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp index 18443141bc..99b9c94abf 100644 --- a/fpdfsdk/fpdf_view_embeddertest.cpp +++ b/fpdfsdk/fpdf_view_embeddertest.cpp @@ -1614,7 +1614,7 @@ TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithAndWithoutExternalMemory) { TestRenderPageBitmapWithExternalMemoryAndNoStride(page.get(), FPDFBitmap_BGR, kBGRBasename); - const char* kGrayBasename = "many_rectangles_grayscale_memory"; + constexpr char kGrayBasename[] = "many_rectangles_grayscale_memory"; TestRenderPageBitmapWithInternalMemory(page.get(), FPDFBitmap_Gray, kGrayBasename, /*fuzzy=*/true); @@ -2052,6 +2052,7 @@ TEST_F(FPDFViewEmbedderTest, RenderXfaPageToSkp) { #if defined(PDF_USE_AGG) && defined(PDF_USE_SKIA) TEST_F(FPDFViewEmbedderTest, Bug2087) { FPDF_DestroyLibrary(); + auto* env = EmbedderTestEnvironment::GetInstance(); std::string agg_checksum; const FPDF_LIBRARY_CONFIG kAggConfig = { @@ -2061,7 +2062,7 @@ TEST_F(FPDFViewEmbedderTest, Bug2087) { .m_v8EmbedderSlot = 0, .m_pPlatform = nullptr, .m_RendererType = FPDF_RENDERERTYPE_AGG, - .m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE, + .m_FontLibraryType = env->GetFontLibraryType(), }; FPDF_InitLibraryWithConfig(&kAggConfig); ASSERT_TRUE(OpenDocument("rectangles.pdf")); @@ -2075,10 +2076,13 @@ TEST_F(FPDFViewEmbedderTest, Bug2087) { std::string skia_checksum; const FPDF_LIBRARY_CONFIG kSkiaConfig = { - .version = 2, + .version = 5, .m_pUserFontPaths = nullptr, .m_pIsolate = nullptr, .m_v8EmbedderSlot = 0, + .m_pPlatform = nullptr, + .m_RendererType = FPDF_RENDERERTYPE_SKIA, + .m_FontLibraryType = env->GetFontLibraryType(), }; FPDF_InitLibraryWithConfig(&kSkiaConfig); ASSERT_TRUE(OpenDocument("rectangles.pdf")); @@ -2091,22 +2095,73 @@ TEST_F(FPDFViewEmbedderTest, Bug2087) { EXPECT_NE(agg_checksum, skia_checksum); - EmbedderTestEnvironment::GetInstance()->TearDown(); - EmbedderTestEnvironment::GetInstance()->SetUp(); + env->TearDown(); + env->SetUp(); } #endif // defined(PDF_USE_AGG) && defined(PDF_USE_SKIA) -TEST_F(FPDFViewEmbedderTest, BadIsolatePerDocumentConfig) { +#if defined(PDF_ENABLE_FREETYPE) && defined(PDF_ENABLE_FONTATIONS) +TEST_F(FPDFViewEmbedderTest, ToggleFreetypeFontationsInit) { + FPDF_DestroyLibrary(); + auto* env = EmbedderTestEnvironment::GetInstance(); + + std::string freetype_checksum; + const FPDF_LIBRARY_CONFIG kFreetypeConfig = { + .version = 5, + .m_pUserFontPaths = nullptr, + .m_pIsolate = nullptr, + .m_v8EmbedderSlot = 0, + .m_pPlatform = nullptr, + .m_RendererType = env->GetRendererType(), + .m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE, + }; + FPDF_InitLibraryWithConfig(&kFreetypeConfig); + ASSERT_TRUE(OpenDocument("text_color.pdf")); + { + ScopedPage page = LoadScopedPage(0); + ScopedFPDFBitmap bitmap = RenderPage(page.get()); + freetype_checksum = HashBitmap(bitmap.get()); + } + CloseDocument(); FPDF_DestroyLibrary(); + std::string fontations_checksum; + const FPDF_LIBRARY_CONFIG kFontationsConfig = { + .version = 5, + .m_pUserFontPaths = nullptr, + .m_pIsolate = nullptr, + .m_v8EmbedderSlot = 0, + .m_pPlatform = nullptr, + .m_RendererType = env->GetRendererType(), + .m_FontLibraryType = FPDF_FONTBACKENDTYPE_FONTATIONS, + }; + FPDF_InitLibraryWithConfig(&kFontationsConfig); + ASSERT_TRUE(OpenDocument("text_color.pdf")); + { + ScopedPage page = LoadScopedPage(0); + ScopedFPDFBitmap bitmap = RenderPage(page.get()); + fontations_checksum = HashBitmap(bitmap.get()); + } + CloseDocument(); + + EXPECT_NE(freetype_checksum, fontations_checksum); + + env->TearDown(); + env->SetUp(); +} +#endif // defined(PDF_USE_AGG) && defined(PDF_USE_SKIA) + +TEST_F(FPDFViewEmbedderTest, BadIsolatePerDocumentConfig) { + FPDF_DestroyLibrary(); + auto* env = EmbedderTestEnvironment::GetInstance(); const FPDF_LIBRARY_CONFIG kBadConfig = { .version = 7, .m_pUserFontPaths = nullptr, .m_pIsolate = reinterpret_cast(1), .m_v8EmbedderSlot = 0, .m_pPlatform = nullptr, - .m_RendererType = FPDF_RENDERERTYPE_AGG, - .m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE, + .m_RendererType = env->GetRendererType(), + .m_FontLibraryType = env->GetFontLibraryType(), .m_BrotliEnabled = false, .m_IsolatePerDocument = true, }; @@ -2117,7 +2172,8 @@ TEST_F(FPDFViewEmbedderTest, BadIsolatePerDocumentConfig) { } TEST_F(FPDFViewEmbedderTest, NoSmoothTextItalicOverlappingGlyphs) { - if (EmbedderTestEnvironment::GetInstance()->fontations()) { + auto* env = EmbedderTestEnvironment::GetInstance(); + if (env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS) { // Monochrome 1-bit text rendering (FPDF_RENDER_NO_SMOOTHTEXT) produces // binary 0/255 pixel transitions at glyph boundaries where subpixel contour // differences cross pixel centers, which cannot be fuzzy matched. diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp index 251ad38757..bb3729bf38 100644 --- a/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp +++ b/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp @@ -30,15 +30,15 @@ TEST_F(CPDFXFAContextEmbedderTest, HasHeap) { TEST_F(CPDFXFAContextEmbedderTest, UnattachedPDFiumCreatedIsolate) { FPDF_DestroyLibrary(); - + auto* env = EmbedderTestEnvironment::GetInstance(); const FPDF_LIBRARY_CONFIG kConfig = { .version = 7, .m_pUserFontPaths = nullptr, .m_pIsolate = nullptr, .m_v8EmbedderSlot = 0, .m_pPlatform = V8TestEnvironment::GetInstance()->platform(), - .m_RendererType = FPDF_RENDERERTYPE_AGG, - .m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE, + .m_RendererType = env->GetRendererType(), + .m_FontLibraryType = env->GetFontLibraryType(), .m_BrotliEnabled = false, .m_IsolatePerDocument = false, }; @@ -59,12 +59,13 @@ TEST_F(CPDFXFAContextEmbedderTest, UnattachedPDFiumCreatedIsolate) { FPDF_DestroyLibrary(); - EmbedderTestEnvironment::GetInstance()->TearDown(); - EmbedderTestEnvironment::GetInstance()->SetUp(); + env->TearDown(); + env->SetUp(); } TEST_F(CPDFXFAContextEmbedderTest, IsolatePerDocument) { FPDF_DestroyLibrary(); + auto* env = EmbedderTestEnvironment::GetInstance(); const FPDF_LIBRARY_CONFIG kConfig = { .version = 7, @@ -72,8 +73,8 @@ TEST_F(CPDFXFAContextEmbedderTest, IsolatePerDocument) { .m_pIsolate = nullptr, .m_v8EmbedderSlot = 0, .m_pPlatform = V8TestEnvironment::GetInstance()->platform(), - .m_RendererType = FPDF_RENDERERTYPE_AGG, - .m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE, + .m_RendererType = env->GetRendererType(), + .m_FontLibraryType = env->GetFontLibraryType(), .m_BrotliEnabled = false, .m_IsolatePerDocument = true, }; @@ -94,6 +95,6 @@ TEST_F(CPDFXFAContextEmbedderTest, IsolatePerDocument) { FPDF_DestroyLibrary(); - EmbedderTestEnvironment::GetInstance()->TearDown(); - EmbedderTestEnvironment::GetInstance()->SetUp(); + env->TearDown(); + env->SetUp(); } diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn index 849fa58568..1a136fd913 100644 --- a/fxjs/BUILD.gn +++ b/fxjs/BUILD.gn @@ -223,6 +223,7 @@ if (pdf_enable_v8) { source_set("gc") { sources = [ "gc/container_trace.h", + "gc/containers.h", "gc/gced_tree_node.h", "gc/gced_tree_node_mixin.h", "gc/heap.cpp", @@ -262,6 +263,7 @@ if (pdf_enable_v8) { if (pdf_enable_xfa) { sources += [ "gc/container_trace_unittest.cpp", + "gc/containers_unittest.cpp", "gc/gced_tree_node_mixin_unittest.cpp", "gc/gced_tree_node_unittest.cpp", "gc/heap_unittest.cpp", diff --git a/fxjs/gc/containers.h b/fxjs/gc/containers.h new file mode 100644 index 0000000000..f967037176 --- /dev/null +++ b/fxjs/gc/containers.h @@ -0,0 +1,96 @@ +// Copyright 2026 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_CONTAINERS_H_ +#define FXJS_GC_CONTAINERS_H_ + +#include +#include +#include +#include +#include + +#include "fxjs/gc/container_trace.h" +#include "v8/include/cppgc/garbage-collected.h" +#include "v8/include/cppgc/member.h" +#include "v8/include/cppgc/type-traits.h" +#include "v8/include/cppgc/visitor.h" + +// These container wrappers are intended to replace stack-local containers +// (e.g. `std::vector`, `std::set`, `std::map`) of garbage-collected objects. +// On the stack, instances should generally be held via `cppgc::Persistent<>`. +// Although holding `cppgc::Persistent<>` handles on the stack is typically an +// anti-pattern in garbage-collected environments, it is required here due to +// the "malloc'd" backing-stores. Holding a single persistent handle +// to a wrapper minimizes overhead compared to holding individual handles for +// every element. + +namespace fxgc { +namespace internal { + +template +using MemberIfNeeded = + std::conditional_t, + cppgc::Member, + T>; + +} // namespace internal + +template +class GCedVector final : public cppgc::GarbageCollected> { + public: + template + void Trace(V* visitor) const { + ContainerTrace(visitor, storage); + } + + std::vector> storage; +}; + +template +class GCedSet final : public cppgc::GarbageCollected> { + public: + template + void Trace(V* visitor) const { + ContainerTrace(visitor, storage); + } + + std::set> storage; +}; + +template +class GCedList final : public cppgc::GarbageCollected> { + public: + template + void Trace(V* visitor) const { + ContainerTrace(visitor, storage); + } + + std::list> storage; +}; + +template +class GCedMap final : public cppgc::GarbageCollected> { + public: + static_assert(cppgc::IsGarbageCollectedOrMixinTypeV || + cppgc::IsGarbageCollectedOrMixinTypeV, + "Use std::map instead for non-gc'd key-value pairs"); + + template + void Trace(V* visitor) const { + ContainerTrace(visitor, storage); + } + + std::map, internal::MemberIfNeeded> + storage; +}; + +} // namespace fxgc + +using fxgc::GCedList; +using fxgc::GCedMap; +using fxgc::GCedSet; +using fxgc::GCedVector; + +#endif // FXJS_GC_CONTAINERS_H_ diff --git a/fxjs/gc/containers_unittest.cpp b/fxjs/gc/containers_unittest.cpp new file mode 100644 index 0000000000..232f670b7a --- /dev/null +++ b/fxjs/gc/containers_unittest.cpp @@ -0,0 +1,110 @@ +// Copyright 2026 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/containers.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 "v8/include/cppgc/allocation.h" +#include "v8/include/cppgc/garbage-collected.h" +#include "v8/include/cppgc/persistent.h" + +namespace { + +class ObservableGCedObject + : public cppgc::GarbageCollected, + public Observable { + public: + CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + + void Trace(cppgc::Visitor* visitor) const {} + + private: + ObservableGCedObject() = default; +}; + +} // namespace + +class ContainersUnitTest : public FXGCUnitTest { + public: + ContainersUnitTest() = default; + ~ContainersUnitTest() override = default; + + ObservableGCedObject* CreateObject() { + return cppgc::MakeGarbageCollected( + heap()->GetAllocationHandle()); + } +}; + +TEST_F(ContainersUnitTest, GCedVector) { + cppgc::Persistent> vec = + cppgc::MakeGarbageCollected>( + heap()->GetAllocationHandle()); + + ObservableGCedObject* obj = CreateObject(); + ObservedPtr observer(obj); + vec->storage.push_back(obj); + + FXGC_ForceGarbageCollection(heap()); + EXPECT_TRUE(observer); + + vec.Clear(); + FXGC_ForceGarbageCollection(heap()); + EXPECT_FALSE(observer); +} + +TEST_F(ContainersUnitTest, GCedSet) { + cppgc::Persistent> set = + cppgc::MakeGarbageCollected>( + heap()->GetAllocationHandle()); + + ObservableGCedObject* obj = CreateObject(); + ObservedPtr observer(obj); + set->storage.insert(obj); + + FXGC_ForceGarbageCollection(heap()); + EXPECT_TRUE(observer); + + set.Clear(); + FXGC_ForceGarbageCollection(heap()); + EXPECT_FALSE(observer); +} + +TEST_F(ContainersUnitTest, GCedList) { + cppgc::Persistent> list = + cppgc::MakeGarbageCollected>( + heap()->GetAllocationHandle()); + + ObservableGCedObject* obj = CreateObject(); + ObservedPtr observer(obj); + list->storage.push_back(obj); + + FXGC_ForceGarbageCollection(heap()); + EXPECT_TRUE(observer); + + list.Clear(); + FXGC_ForceGarbageCollection(heap()); + EXPECT_FALSE(observer); +} + +TEST_F(ContainersUnitTest, GCedMap) { + cppgc::Persistent> map = + cppgc::MakeGarbageCollected>( + heap()->GetAllocationHandle()); + + ObservableGCedObject* obj = CreateObject(); + ObservedPtr observer(obj); + map->storage[123] = obj; + + FXGC_ForceGarbageCollection(heap()); + EXPECT_TRUE(observer); + + map.Clear(); + FXGC_ForceGarbageCollection(heap()); + EXPECT_FALSE(observer); +} diff --git a/fxjs/gc/heap.cpp b/fxjs/gc/heap.cpp index 0a7bfddef8..119e9cdf04 100644 --- a/fxjs/gc/heap.cpp +++ b/fxjs/gc/heap.cpp @@ -139,6 +139,9 @@ FXGCScopedHeap FXGC_CreateHeap(v8::Isolate* isolate) { std::make_shared(), cppgc::Heap::HeapOptions{ {}, + // Do not enable conservative stack scanning. STL containers have + // backing stores that are "malloc'd" and therefore scanning will + // *not* try to trace them. cppgc::Heap::StackSupport::kNoConservativeStackScan, cppgc::Heap::MarkingType::kAtomic, cppgc::Heap::SweepingType::kIncrementalAndConcurrent, diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp index 60645070c9..5981872810 100644 --- a/fxjs/xfa/cfxjse_engine.cpp +++ b/fxjs/xfa/cfxjse_engine.cpp @@ -191,6 +191,13 @@ CFXJSE_Engine::EventParamScope::~EventParamScope() { engine_->event_param_ = prev_event_param_; } +CFXJSE_Engine::ScriptNodeArrayScope::ScriptNodeArrayScope( + CFXJSE_Engine* engine, + std::vector>* array) + : restorer_(&engine->script_node_array_, array) {} + +CFXJSE_Engine::ScriptNodeArrayScope::~ScriptNodeArrayScope() = default; + // The runtime belongs to the form fill environment, which may depart before // this object does, so look it up rather than holding it. CJS_Runtime* CFXJSE_Engine::GetCJSRuntime() const { @@ -951,11 +958,6 @@ v8::Local CFXJSE_Engine::GetOrCreateJSBindingFromMap( return NewNormalXFAObject(pObject); } -void CFXJSE_Engine::SetNodesOfRunScript( - std::vector>* pArray) { - script_node_array_ = pArray; -} - void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) { if (script_node_array_ && !pdfium::Contains(*script_node_array_, pNode, diff --git a/fxjs/xfa/cfxjse_engine.h b/fxjs/xfa/cfxjse_engine.h index fafc729af0..4cccc257b1 100644 --- a/fxjs/xfa/cfxjse_engine.h +++ b/fxjs/xfa/cfxjse_engine.h @@ -12,6 +12,7 @@ #include #include +#include "core/fxcrt/autorestorer.h" #include "core/fxcrt/mask.h" #include "core/fxcrt/unowned_ptr.h" #include "fxjs/cfx_isolate_wrapper.h" @@ -129,6 +130,19 @@ class CFXJSE_Engine final : public CFX_IsolateWrapper { }; friend class EventParamScope; + class ScriptNodeArrayScope { + CPPGC_STACK_ALLOCATED(); + + public: + ScriptNodeArrayScope(CFXJSE_Engine* engine, + std::vector>* array); + ~ScriptNodeArrayScope(); + + private: + AutoRestorer>>> + restorer_; + }; + CXFA_Node* GetEventTarget() const { return target_; } CXFA_EventParam* GetEventParam() const { return event_param_; } @@ -152,7 +166,6 @@ class CFXJSE_Engine final : public CFX_IsolateWrapper { CFXJSE_Class* GetJseNormalClass() const { return js_class_; } CXFA_Document* GetDocument() const { return document_.Get(); } - void SetNodesOfRunScript(std::vector>* pArray); void AddNodesOfRunScript(CXFA_Node* pNode); void SetRunAtType(XFA_AttributeValue eRunAt) { run_at_type_ = eRunAt; } diff --git a/pdfium.gni b/pdfium.gni index 7d5811d89c..3b95197340 100644 --- a/pdfium.gni +++ b/pdfium.gni @@ -51,13 +51,6 @@ declare_args() { # and the default renderer is selectable at runtime. pdf_use_skia = pdf_use_skia_override - # When building PDFium with Skia enabled, use a custom font manager if there - # is one. The target that PDFium will depend on must implement the following - # C++ function signature: - # - # sk_sp pdfium_skia_custom_font_manager(); - pdf_skia_font_manager_target = pdf_skia_font_manager_target_override - # Build PDFium with experimental Fontations library support. # If enabled, coexists in build with FreeType library and the default font # library is selectable at runtime. diff --git a/scripts/embedpdf-runtime/build-target.sh b/scripts/embedpdf-runtime/build-target.sh index 3a55676881..19d89b2276 100755 --- a/scripts/embedpdf-runtime/build-target.sh +++ b/scripts/embedpdf-runtime/build-target.sh @@ -21,7 +21,11 @@ case "$TARGET" in wasm32) GN_TARGET_OS="emscripten" GN_TARGET_CPU="wasm" - EXTRA_ARGS=$'\nis_clang=false\nuse_custom_libcxx=false' + # No libyuv: emsdk 3.1.72's clang crashes on its WASM SIMD rows + # (I422ToARGBRow_WASMSIMD, from the Oct 27 2026 roll). PDFium keeps its + # own loops for pdf_use_libyuv=false; they write the same colors, and + # leave the unused byte of a BGRx pixel as it was instead of 0xFF. + EXTRA_ARGS=$'\nis_clang=false\nuse_custom_libcxx=false\npdf_use_libyuv=false' # build/ reads the macOS SDK whenever the host is a Mac (gn_logs.gni), but # PDFium's build_overrides/build.gni decides use_system_xcode only for mac # and iOS targets. @@ -102,6 +106,8 @@ fi OUT="$SOURCE_DIR/out/embedpdf-runtime/$TARGET" mkdir -p "$OUT" +# pdf_enable_brotli=false: /BrotliDecode needs a config at start-up, and +# EPDF_InitThread passes none, so the decoder (about 165 KB) would never run. cat > "$OUT/args.gn" < +#include #include #include #include @@ -1303,16 +1304,18 @@ void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap, // static void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap, + std::string_view expectation_png_name) { + auto* env = EmbedderTestEnvironment::GetInstance(); std::string png_path = GetEmbedderTestPngExpectationPath(expectation_png_name); SCOPED_TRACE(testing::Message() << "CompareBitmap() with " << png_path); DiffOptions options = kExactDiffOptions; - if (EmbedderTestEnvironment::GetInstance()->fontations()) { + if (env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS) { options = kFontationsDiffOptions; } CompareBitmapToPngFile(bitmap, png_path, options); - if (EmbedderTestEnvironment::GetInstance()->write_pngs()) { + if (env->write_results()) { WriteBitmapToPng(bitmap, png_path); } } @@ -1322,8 +1325,9 @@ void EmbedderTest::CompareBitmapWithExpectationSuffix( FPDF_BITMAP bitmap, std::string_view expectation_png_name, const DiffOptions& options) { + auto* env = EmbedderTestEnvironment::GetInstance(); DiffOptions effective_options = options; - if (EmbedderTestEnvironment::GetInstance()->fontations()) { + if (env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS) { effective_options.max_pixel_per_channel_delta = std::max(options.max_pixel_per_channel_delta, kFontationsDiffOptions.max_pixel_per_channel_delta); @@ -1348,7 +1352,7 @@ void EmbedderTest::CompareBitmapWithExpectationSuffix( SCOPED_TRACE(testing::Message() << "CompareBitmapWithExpectationSuffix() with " << png_path); CompareBitmapToPngFile(bitmap, png_path, effective_options); - if (EmbedderTestEnvironment::GetInstance()->write_pngs()) { + if (EmbedderTestEnvironment::GetInstance()->write_results()) { WriteBitmapToPng(bitmap, png_path); } return; @@ -1363,6 +1367,10 @@ void EmbedderTest::CompareDisplayList(const std::string& display_list, std::string path = GetEmbedderTestDisplayListExpectationPath(expectation_name); SCOPED_TRACE(testing::Message() << "CompareDisplayList() with " << path); + if (EmbedderTestEnvironment::GetInstance()->write_results()) { + std::ofstream out(path, std::ios::binary); + out << display_list; + } std::vector expected_data = GetFileContents(path.c_str()); if (expected_data.empty()) { ADD_FAILURE() << "Missing or empty expectation file: " << path; diff --git a/testing/embedder_test_environment.cpp b/testing/embedder_test_environment.cpp index 271ad4e41c..e0d552ecc7 100644 --- a/testing/embedder_test_environment.cpp +++ b/testing/embedder_test_environment.cpp @@ -74,8 +74,8 @@ void EmbedderTestEnvironment::AddFlags(int argc, char** argv) { } void EmbedderTestEnvironment::AddFlag(const std::string& flag) { - if (flag == "--write-pngs") { - write_pngs_ = true; + if (flag == "--write-pngs" || flag == "--write-results") { + write_results_ = true; return; } diff --git a/testing/embedder_test_environment.h b/testing/embedder_test_environment.h index dca8fe5d74..3e12030a06 100644 --- a/testing/embedder_test_environment.h +++ b/testing/embedder_test_environment.h @@ -28,13 +28,13 @@ class EmbedderTestEnvironment : public testing::Environment { void SetVersion(int version) { version_ = version; } bool GetBrotli() const { return brotli_enabled_; } int GetVersion() const { return version_; } + FPDF_RENDERER_TYPE GetRendererType() const { return renderer_type_; } + FPDF_FONT_BACKEND_TYPE GetFontLibraryType() const { + return font_library_type_; + } void AddFlags(int argc, char** argv); - - bool write_pngs() const { return write_pngs_; } - bool fontations() const { - return font_library_type_ == FPDF_FONTBACKENDTYPE_FONTATIONS; - } + bool write_results() const { return write_results_; } private: void AddFlag(const std::string& flag); @@ -42,7 +42,7 @@ class EmbedderTestEnvironment : public testing::Environment { int version_ = 6; FPDF_RENDERER_TYPE renderer_type_; FPDF_FONT_BACKEND_TYPE font_library_type_; - bool write_pngs_ = false; + bool write_results_ = false; bool brotli_enabled_ = false; TestFonts test_fonts_; }; diff --git a/testing/embedpdf_layer_mode_writers.txt b/testing/embedpdf_layer_mode_writers.txt index 3ef2323d36..9c6d7623fb 100644 --- a/testing/embedpdf_layer_mode_writers.txt +++ b/testing/embedpdf_layer_mode_writers.txt @@ -75,7 +75,7 @@ EPDFAttachmentEmbedderTest.ExtractFileLargeAttachment 5 EPDFAttachmentEmbedderTest.GetAttachmentIndexByKey 6 EPDFFlattenEmbedderTest.FlattenAnnotationSetReportsPerEntry 7 EPDFFlattenEmbedderTest.FlattenForDisplayLeavesANoViewAnnotation 1 -EPDFFlattenEmbedderTest.FlattenMergedWidgetRemovesFieldTreeEntry 12 +EPDFFlattenEmbedderTest.FlattenMergedWidgetRemovesFieldTreeEntry 11 EPDFFlattenEmbedderTest.FlattenPageHonorsPrintUsage 10 EPDFFlattenEmbedderTest.FlattenPagePreservesUnpaintedAnnotationsAndDetachesWidget 12 EPDFFlattenEmbedderTest.FlattenSpecificAnnotationByHandle 7 diff --git a/testing/resources/arabic_actual_text_ordering.in b/testing/resources/arabic_actual_text_ordering.in new file mode 100644 index 0000000000..c538ff7e8c --- /dev/null +++ b/testing/resources/arabic_actual_text_ordering.in @@ -0,0 +1,229 @@ +{{header}} +{{object 1 0}} << + /Type /Catalog + /Pages 2 0 R +>> +endobj +{{object 2 0}} << + /Type /Pages + /Count 4 + /Kids [3 0 R 4 0 R 5 0 R 6 0 R] + /MediaBox [0 0 200 200] + /Resources << + /Font << + /FXF1 11 0 R + >> + >> +>> +endobj +{{object 3 0}} << + /Type /Page + /Parent 2 0 R + /Contents 7 0 R +>> +endobj +{{object 4 0}} << + /Type /Page + /Parent 2 0 R + /Contents 8 0 R +>> +endobj +{{object 5 0}} << + /Type /Page + /Parent 2 0 R + /Contents 9 0 R +>> +endobj +{{object 6 0}} << + /Type /Page + /Parent 2 0 R + /Contents 10 0 R +>> +endobj +% Page 0: Variant 1 - Visual order of spans, diacritic first within span +{{object 7 0}} << + {{streamlen}} +>> +stream +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 100.41156 98.06628 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 99.921432 102.05713 Tm /FXF1 9 Tf 0 Tr [<05>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 104.315842 101.99542 Tm /FXF1 9 Tf 0 Tr [<02>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 102.958832 102.05713 Tm /FXF1 9 Tf 0 Tr [<04>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.798752 99.43768 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.730003 102.05713 Tm /FXF1 9 Tf 0 Tr [<03>] TJ ET Q +EMC +endstream +endobj +% Page 1: Variant 2 - Visual order of spans, base char first within span +{{object 8 0}} << + {{streamlen}} +>> +stream +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 99.921432 102.05713 Tm /FXF1 9 Tf 0 Tr [<05>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 100.41156 98.06628 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 102.958832 102.05713 Tm /FXF1 9 Tf 0 Tr [<04>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 104.315842 101.99542 Tm /FXF1 9 Tf 0 Tr [<02>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.730003 102.05713 Tm /FXF1 9 Tf 0 Tr [<03>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.798752 99.43768 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +EMC +endstream +endobj +% Page 2: Variant 3 - Logical order of spans, diacritic first within span +{{object 9 0}} << + {{streamlen}} +>> +stream +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.798752 99.43768 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.730003 102.05713 Tm /FXF1 9 Tf 0 Tr [<03>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 104.315842 101.99542 Tm /FXF1 9 Tf 0 Tr [<02>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 102.958832 102.05713 Tm /FXF1 9 Tf 0 Tr [<04>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 100.41156 98.06628 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 99.921432 102.05713 Tm /FXF1 9 Tf 0 Tr [<05>] TJ ET Q +EMC +endstream +endobj +% Page 3: Variant 4 - Logical order of spans, base char first within span +{{object 10 0}} << + {{streamlen}} +>> +stream +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.730003 102.05713 Tm /FXF1 9 Tf 0 Tr [<03>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 107.798752 99.43768 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 102.958832 102.05713 Tm /FXF1 9 Tf 0 Tr [<04>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 104.315842 101.99542 Tm /FXF1 9 Tf 0 Tr [<02>] TJ ET Q +EMC +/Span <>> BDC +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 99.921432 102.05713 Tm /FXF1 9 Tf 0 Tr [<05>] TJ ET Q +q 0 0 0 rg 0 0 0 RG BT 1 0 0 1 100.41156 98.06628 Tm /FXF1 9 Tf 0 Tr [<01>] TJ ET Q +EMC +endstream +endobj +{{object 11 0}} << + /Type /Font + /Subtype /TrueType + /BaseFont /BAAAAA+NotoSansArabic-Regular + /FirstChar 1 + /FontDescriptor 12 0 R + /LastChar 5 + /ToUnicode 13 0 R + /Widths [0 0 269 850 562] +>> +endobj +{{object 12 0}} << + /Type /FontDescriptor + /Ascent 1374 + /CapHeight 1374 + /Descent -738 + /Flags 4 + /FontBBox [-220 -548 7175 1359] + /FontFile2 14 0 R + /FontName /BAAAAA+NotoSansArabic-Regular + /ItalicAngle 0 + /StemV 80 +>> +endobj +{{object 13 0}} << + {{streamlen}} +>> +stream +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo << + /Registry (Adobe) + /Ordering (UCS) + /Supplement 0 +>> def +/CMapName /Adobe-Identity-UCS def +/CMapType 2 def +1 begincodespacerange + <00> +endcodespacerange +5 beginbfchar + <01> <0650> + <02> <0652> + <03> + <04> + <05> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +endstream +endobj +% Font: NotoSansArabic-Regular Version 2.005 +% URL: https://github.com/notofonts/arabic +% License: SIL Open Font License 1.1 (OFL-1.1) +% +% Step 1: Subset the 5 glyphs used in this test: +% $ pyftsubset NotoSansArabic-Regular.ttf \ +% --unicodes="U+0650,U+0652,U+FE91,U+FEB4,U+FEE2" \ +% --output-file=subset.ttf +% +% Step 2: Add a format 0 cmap mapping byte codes 1-5 to those glyphs for simple +% TrueType font support: +% $ python3 -c ' +% from fontTools.ttLib import TTFont +% from fontTools.ttLib.tables._c_m_a_p import CmapSubtable +% f = TTFont("subset.ttf") +% sub = CmapSubtable.newSubtable(0) +% sub.platformID, sub.platEncID, sub.language = 1, 0, 0 +% sub.cmap = {1:"uni0650", 2:"uni0652", 3:"uniFE91", 4:"uniFEB4", 5:"uniFEE2"} +% f["cmap"].tables.append(sub) +% f.save("subset.ttf")' +{{object 14 0}} << + /Filter [/ASCII85Decode /FlateDecode] + {{streamlen}} + /Length1 2748 +>> +stream +GhV7[92&Sj)#hjpo4!5Y?s_&21CO]99VKgAQl2Pnb+$h2]N]2UjAMkf+,-E8-B6#'WTh:,WN"Tn@GJBW +_M+sp\568H(cCTqW/45YU'kIQ@FD$=nN5o?>"VJIC[]OD+6WS%J,]J[kAc=jObI*cR3oL,3kK'cr'[Ce +k\]W,kT>9FlA]eXX8@ggF;PqTTs)nua^S3#ecscc"ZOYkJk[cQ.l-8#Xkn!!M5`[NKT2O_XdPpY[!St)\ +Cp&kjVD*H)H=07JRNoO'ql4nQI5^W,)($Y?o:cqNHm/0Bf)OoNrATrj1Rq +2i+n#j@aLs58,F#rVbQR`A"N=?d1l$0&9TM!]eH\ig!F`@Z@^!aKLdtE2!WE4QTJ"bWL57`EL$X26W>5 +#q^'KkthVg`l.I,$lF`-Y&1k]F[)&j-p+ekG95UP.r:H(G2pCI*'Z7&1=?.#ai8Wd5_jo.MH8qNk%/D2 +O,.;6Br1:OFX\TM!`iOqA)QRmFO5`c47FIrBdaQhf#F3i]:mj8o/Z/$hZl?Wh=%i-I:<@j*q9N#8*$V, +-'O11-sYr%?uE.#mg2#[F'[@BrD677H"a=r*uGpc:Q9>.Lif/TY`CKA/Q(\eglmcM[l%h2=UXomQ05(r +&Mj[:bi5jPSbaLreQRX=,FMD@b'Y?MYtjMd8"u/DmY+:*U(Y@2^?3[E)q#FiUC+$=4Om=RhCPE=Vop`m +2$HW?@08r"Be)&"*:UZQK>FHKL*B[bsh,b ++^0Dl'=*@n]IWSFn3`D(_cPP?R;m5Z3[h%'>2HgIB%lj8OZ9Ss7n'k%(gD`;i0auTLm4dcYZhnuB4/+mOG]^F)HbRce^>N2)N'h?pWQAB''kLpnoJE>>J]`dkO!A0I&BY/7rMI/B>97 +1u$R6:h-)#:/@`&Q8b&Y*-=W5aj02)V:i4Q..L&l7oc.:Wd])-.dr=q]Ig:GCO09Q\2P;VI3(@T_ebe#!pMf*F&StbciUm]p'eAUT8TllBP@\&mn(2<*&1]H?"CR*pDpC7lC4Hi([[q +EN'i#^\scJcsrdrB`e2\nE)f4;KKksLkmI54(KDo.,('X`%HM%-iVQ$Cq8=IipMTO!j5$LihXmeja'=O +A#'8d^%e2oS%q3sF=f9LH\n%\xa0\xf2\xa4\xf4' XREF_TOKEN = b'{{xref}}' XREF_REPLACEMENT = b'xref\n%d %d\n' @@ -121,8 +127,10 @@ def preprocess_line(self, line): self.streamlens[-1] += len(line) def process_line(self, line): - if self.HEADER_TOKEN in line: - line = line.replace(self.HEADER_TOKEN, self.HEADER_REPLACEMENT) + line = self.HEADER_PATTERN.sub(self.HEADER_REPLACEMENT, line) + if self.FIXED_HEADER_TOKEN in line: + line = line.replace(self.FIXED_HEADER_TOKEN, + self.FIXED_HEADER_REPLACEMENT) if self.STREAMLEN_TOKEN in line: sub = self.STREAMLEN_REPLACEMENT % self.streamlens.pop(0) line = re.sub(self.STREAMLEN_TOKEN, sub, line) diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium index dc913be409..6cc4a68e94 100644 --- a/third_party/lcms/README.pdfium +++ b/third_party/lcms/README.pdfium @@ -1,7 +1,7 @@ Name: Little CMS URL: https://github.com/mm2/Little-CMS -Version: 2.19 -Revision: b76633e60c8387a77268fb3359277ca25b5fd75c +Version: lcms2.19.1 +Revision: 21c582a594fe5279f90c0b93437c398f93bf62b0 Update Mechanism: Manual Security Critical: yes Shipped: yes diff --git a/third_party/lcms/crowbar.txtpb b/third_party/lcms/crowbar.txtpb index d851af426e..37a78360dd 100644 --- a/third_party/lcms/crowbar.txtpb +++ b/third_party/lcms/crowbar.txtpb @@ -1,9 +1,9 @@ upstream { - commit: "b76633e60c8387a77268fb3359277ca25b5fd75c" + version: "lcms2.19.1" artifacts { git { repository: "https://github.com/mm2/Little-CMS" - commit: "b76633e60c8387a77268fb3359277ca25b5fd75c" + commit: "21c582a594fe5279f90c0b93437c398f93bf62b0" } } } @@ -13,3 +13,15 @@ stages { } publish { git {} } } +latest { + generic { + format: "lcms${n0}.${n1}${n2}" + regex_groups { key: "n0" value: "[0-9]+" } + regex_groups { key: "n1" value: "[0-9]+" } + regex_groups { key: "n2" value: "(\\.[0-9]+)?" } + } + git { + repository: "https://github.com/mm2/Little-CMS" + tags {} + } +} diff --git a/third_party/lcms/src/src/cmscgats.c b/third_party/lcms/src/src/cmscgats.c index 8b5aa768ce..490be7bcc9 100644 --- a/third_party/lcms/src/src/cmscgats.c +++ b/third_party/lcms/src/src/cmscgats.c @@ -1687,8 +1687,8 @@ cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) return FALSE; } - if (n >= t -> nSamples) { - SynError(it8, "More than NUMBER_OF_FIELDS fields."); + if (n < 0 || n >= t -> nSamples) { + SynError(it8, "Invalid or more than NUMBER_OF_FIELDS fields."); return FALSE; } @@ -1701,9 +1701,11 @@ cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) } -cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) +cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) { cmsIT8* it8 = (cmsIT8*)h; + + _cmsAssert(n >= 0); return SetDataFormat(it8, n, Sample); } diff --git a/third_party/lcms/src/src/cmsio0.c b/third_party/lcms/src/src/cmsio0.c index e00a0d27d2..16181c3059 100644 --- a/third_party/lcms/src/src/cmsio0.c +++ b/third_party/lcms/src/src/cmsio0.c @@ -214,10 +214,12 @@ cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, con if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing - // Check for available space. + // Check for available space. Truncate the output in case the space + // is not enough instead of erroring out. See + // https://github.com/hughsie/colord/issues/147. - if (size > ResData->Size || ResData->Pointer > (ResData->Size - size)) - goto WriteError; + if (size > ResData->Size - ResData->Pointer) + size = ResData->Size - ResData->Pointer; memmove(ResData ->Block + ResData ->Pointer, Ptr, size); ResData ->Pointer += size; diff --git a/tools/roll_pdfium_deps.py b/tools/roll_pdfium_deps.py index 6fb3684776..2a1501bee1 100755 --- a/tools/roll_pdfium_deps.py +++ b/tools/roll_pdfium_deps.py @@ -31,11 +31,11 @@ 'libcxx_revision', 'libcxxabi_revision', 'libunwind_revision', + 'libyuv_revision', 'llvm_libc_revision', 'nasm_source_revision', 'ninja_version', 'partition_allocator_revision', - 'reclient_version', 'result_adapter_revision', 'rust_revision', 'siso_version', diff --git a/tools/roll_pdfium_deps_test.py b/tools/roll_pdfium_deps_test.py index 7d0987eb71..43830de6ad 100644 --- a/tools/roll_pdfium_deps_test.py +++ b/tools/roll_pdfium_deps_test.py @@ -1131,9 +1131,6 @@ 'src/third_party/ink/src': Var('chromium_git') + '/external/github.com/google/ink.git' + '@' + Var('ink_revision'), - 'src/third_party/ink_stroke_modeler/src': - Var('chromium_git') + '/external/github.com/google/ink-stroke-modeler.git' + '@' + Var('ink_stroke_modeler_revision'), - 'src/third_party/instrumented_libs': { 'url': Var('chromium_git') + '/chromium/third_party/instrumented_libraries.git' + '@' + '69015643b3f68dbd438c010439c59adc52cac808', 'condition': 'checkout_instrumented_libraries', @@ -1157,6 +1154,9 @@ 'src/third_party/libjpeg_turbo': Var('chromium_git') + '/chromium/deps/libjpeg_turbo.git' + '@' + '6383cf609c1f63c18af0f59b2738caa0c6c7e379', + 'src/third_party/libyuv': + Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '1e2e3a35993609da1d89ac170b907719d555f4b3', + 'src/third_party/lss': { 'url': Var('chromium_git') + '/linux-syscall-support.git' + '@' + Var('lss_revision'), 'condition': 'checkout_android or checkout_linux', @@ -1746,6 +1746,10 @@ # and whatever else without interference from each other. 'libunwind_revision': '14b9dee79c12dd8cdd3d662ee0be49200c6d9b71', # Three lines of non-changing comments so that + # the commit queue can handle CLs rolling libyuv + # and whatever else without interference from each other. + 'libyuv_revision': 'eb8eda9c7973704d1103a535059224df7a6063b5', + # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. 'llvm_libc_revision': '006672b9b6481bae04b9428100ed0486ab99f452', @@ -2033,6 +2037,9 @@ 'condition': 'checkout_libpng', }, + 'third_party/libyuv': + Var('chromium_git') + '/libyuv/libyuv.git@' + Var('libyuv_revision'), + 'third_party/llvm-build/Release+Asserts': { 'dep_type': 'gcs', 'bucket': 'chromium-browser-clang', @@ -2547,10 +2554,6 @@ def testCipdDepsRollsNothingToRoll(self): self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = _run_roll_dep('reclient_version') - self.assertTrue(success) - self.assertEqual('Revisions are the same.', message) - success, message = _run_roll_dep('result_adapter_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) @@ -2616,6 +2619,13 @@ def testGitDepsRollsToChromiumRevision(self): '--roll-to 88fc07ed143a5b3bbf45d430b72a4617ee9e235f ' '--ignore-dirty-tree --no-log', message) + success, message = _run_roll_dep('libyuv_revision') + self.assertTrue(success) + self.assertEqual( + 'roll-dep third_party/libyuv ' + '--roll-to 1e2e3a35993609da1d89ac170b907719d555f4b3 ' + '--ignore-dirty-tree --no-log', message) + success, message = _run_roll_dep('llvm_libc_revision') self.assertTrue(success) self.assertEqual( @@ -2772,6 +2782,9 @@ def testRollAll(self): 'roll-dep third_party/libunwind/src ' '--roll-to 88fc07ed143a5b3bbf45d430b72a4617ee9e235f ' '--ignore-dirty-tree --no-log', + 'roll-dep third_party/libyuv ' + '--roll-to 1e2e3a35993609da1d89ac170b907719d555f4b3 ' + '--ignore-dirty-tree --no-log', 'roll-dep third_party/llvm-libc/src ' '--roll-to 03f822d2a88c8f68f6a92c5cb3e79ccc3002e8a9 ' '--ignore-dirty-tree --no-log', diff --git a/xfa/fwl/cfwl_edit_embeddertest.cpp b/xfa/fwl/cfwl_edit_embeddertest.cpp index 1c43f01edf..6e9f200f5d 100644 --- a/xfa/fwl/cfwl_edit_embeddertest.cpp +++ b/xfa/fwl/cfwl_edit_embeddertest.cpp @@ -166,6 +166,12 @@ TEST_F(CFWLEditEmbedderTest, FillWithNewLineWithoutMultiline) { } TEST_F(CFWLEditEmbedderTest, FillWithNewLineWithMultiline) { + auto* env = EmbedderTestEnvironment::GetInstance(); + static constexpr DiffOptions kMultilineDiffOptions = { + .max_pixel_per_channel_delta = 255, + .max_mean_squared_error = 5.0, + }; + CreateAndInitializeFormPDF("xfa/xfa_multiline_textfield.pdf"); ScopedPage page = LoadScopedPage(0); FORM_OnLButtonDown(form_handle(), page.get(), 0, 115, 58); @@ -186,8 +192,11 @@ TEST_F(CFWLEditEmbedderTest, FillWithNewLineWithMultiline) { "xfa_multiline_textfield_filled"; ScopedFPDFBitmap page_bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - kFilledMultilineBasename); + CompareBitmapWithExpectationSuffix( + page_bitmap.get(), kFilledMultilineBasename, + env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS + ? kMultilineDiffOptions + : kExactDiffOptions); } for (size_t i = 0; i < 4; ++i) { @@ -211,8 +220,11 @@ TEST_F(CFWLEditEmbedderTest, FillWithNewLineWithMultiline) { "xfa_multiline_textfield_backspace"; ScopedFPDFBitmap page_bitmap = RenderLoadedPageWithFlags(page.get(), FPDF_ANNOT); - CompareBitmapWithExpectationSuffix(page_bitmap.get(), - kMultilineBackspaceBasename); + CompareBitmapWithExpectationSuffix( + page_bitmap.get(), kMultilineBackspaceBasename, + env->GetFontLibraryType() == FPDF_FONTBACKENDTYPE_FONTATIONS + ? kMultilineDiffOptions + : kExactDiffOptions); } } diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp index aad2aef05d..f3730f8105 100644 --- a/xfa/fxfa/parser/cxfa_document.cpp +++ b/xfa/fxfa/parser/cxfa_document.cpp @@ -18,6 +18,7 @@ #include "core/fxcrt/xml/cfx_xmlelement.h" #include "fxjs/cjs_runtime.h" #include "fxjs/gc/container_trace.h" +#include "fxjs/gc/containers.h" #include "fxjs/xfa/cfxjse_engine.h" #include "fxjs/xfa/cfxjse_resolveprocessor.h" #include "fxjs/xfa/cjx_object.h" @@ -1612,28 +1613,32 @@ void CXFA_Document::DoProtoMerge() { return; } - std::map mIDMap; - std::set sUseNodes; + cppgc::Persistent> id_map = + cppgc::MakeGarbageCollected>( + GetHeap()->GetAllocationHandle()); + cppgc::Persistent> use_nodes = + cppgc::MakeGarbageCollected>( + GetHeap()->GetAllocationHandle()); CXFA_NodeIterator sIterator(pTemplateRoot); for (CXFA_Node* pNode = sIterator.GetCurrent(); pNode; pNode = sIterator.MoveToNext()) { WideString wsIDVal = pNode->JSObject()->GetCData(XFA_Attribute::Id); if (!wsIDVal.IsEmpty()) { - mIDMap[FX_HashCode_GetW(wsIDVal.AsStringView())] = pNode; + id_map->storage[FX_HashCode_GetW(wsIDVal.AsStringView())] = pNode; } WideString wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Use); if (!wsUseVal.IsEmpty()) { - sUseNodes.insert(pNode); + use_nodes->storage.push_back(pNode); } else { wsUseVal = pNode->JSObject()->GetCData(XFA_Attribute::Usehref); if (!wsUseVal.IsEmpty()) { - sUseNodes.insert(pNode); + use_nodes->storage.push_back(pNode); } } } - for (CXFA_Node* pUseHrefNode : sUseNodes) { + for (CXFA_Node* pUseHrefNode : use_nodes->storage) { // Must outlive the WideStringViews below. WideString wsUseVal = pUseHrefNode->JSObject()->GetCData(XFA_Attribute::Usehref); @@ -1666,8 +1671,8 @@ void CXFA_Document::DoProtoMerge() { } } } else if (!wsID.IsEmpty()) { - auto it = mIDMap.find(FX_HashCode_GetW(wsID)); - if (it == mIDMap.end()) { + auto it = id_map->storage.find(FX_HashCode_GetW(wsID)); + if (it == id_map->storage.end()) { continue; } pProtoNode = it->second; diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp index f7e2475138..7c0e3e4676 100644 --- a/xfa/fxfa/parser/cxfa_node.cpp +++ b/xfa/fxfa/parser/cxfa_node.cpp @@ -2934,11 +2934,15 @@ CXFA_Node::BoolScriptResult CXFA_Node::ExecuteBoolScript( context, pEventParam->targeted_ ? this : nullptr, pEventParam); context->SetRunAtType(script->GetRunAt()); + // Avoid wasted work in non-calculate events by not attaching a ref node + // array. Always attaching ref nodes would be functionally equivalent, but + // every node whose value is read during a non-calculate script would be + // pinned until ExecuteBoolScript() exits. + const bool needs_ref_nodes = pEventParam->type_ == XFA_EVENT_InitCalculate || + pEventParam->type_ == XFA_EVENT_Calculate; std::vector> refNodes; - if (pEventParam->type_ == XFA_EVENT_InitCalculate || - pEventParam->type_ == XFA_EVENT_Calculate) { - context->SetNodesOfRunScript(&refNodes); - } + CFXJSE_Engine::ScriptNodeArrayScope node_array_scope( + context, needs_ref_nodes ? &refNodes : nullptr); CFXJSE_Context::ExecutionResult exec_result; { @@ -2988,7 +2992,6 @@ CXFA_Node::BoolScriptResult CXFA_Node::ExecuteBoolScript( } } } - context->SetNodesOfRunScript(nullptr); return {iRet, !value.IsEmpty() && fxv8::IsBoolean(value) && fxv8::ReentrantToBooleanHelper(isolate, value)};