diff --git a/.gitignore b/.gitignore index 4e57fbf1f2..074015c802 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,11 @@ /third_party/libunwind/src /third_party/libjpeg_turbo /third_party/libpng +/third_party/libyuv /third_party/llvm /third_party/llvm-build /third_party/llvm-libc/src +/third_party/llvm-libclang /third_party/markupsafe /third_party/nasm /third_party/ninja diff --git a/.gn b/.gn index 13ba5d1c1e..33179129c2 100644 --- a/.gn +++ b/.gn @@ -53,6 +53,10 @@ default_args = { # Adds -Wexit-time-destructors check to match Chromium. is_wexit_time_destructors_default = true + + # Avoid pulling libjpeg_turbo into libyuv in standalone builds. + libyuv_disable_jpeg = true + libyuv_include_tests = false } no_check_targets = [ diff --git a/AUTHORS b/AUTHORS index c8141c9379..eccd868087 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Chenguang Shao Chery Cherian Claudio DeSouza Dan Ilan +Deepak Mohan Dmitrii Chunikhin Dorian Rudolph Felix Kauselmann @@ -29,11 +30,13 @@ Haoran Tang Helmut Januschka Huy Ngo Ivan Odulo +Jan Wytze Zuidema Jeroen Bobbeldijk Jiang Jiang Jimin Lee Justin Pierce Ke Liu +Kei Kamikawa Luật Nguyễn Manuel Geißer Matt Jolly diff --git a/BUILD.gn b/BUILD.gn index d9790cd2fe..a3ecb2e1bd 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -15,15 +15,6 @@ group("default") { } } -group("freetype_common") { - public_deps = [] - if (pdf_bundle_freetype) { - public_deps += [ "third_party:fx_freetype" ] - } else { - public_deps += [ "//build/config/freetype" ] - } -} - config("pdfium_common_config") { cflags = [] cflags_cc = [] @@ -44,6 +35,10 @@ config("pdfium_common_config") { defines += [ "PDF_ENABLE_FONTATIONS" ] } + if (pdf_enable_freetype) { + defines += [ "PDF_ENABLE_FREETYPE" ] + } + if (pdf_enable_rust_bmp) { defines += [ "PDF_ENABLE_RUST_BMP" ] } @@ -67,6 +62,10 @@ config("pdfium_common_config") { defines += [ "EPDF_THREAD_LOCAL_GLOBALS" ] } + if (pdf_use_libyuv) { + defines += [ "PDF_USE_LIBYUV" ] + } + if (is_win) { # Assume UTF-8 by default to avoid code page dependencies. cflags += [ "/utf-8" ] @@ -269,6 +268,7 @@ source_set("pdfium_public_headers_impl") { "public/fpdf_transformpage.h", "public/fpdfview.h", ] + visibility = [ ":*" ] } group("pdfium_public_headers") { @@ -333,6 +333,17 @@ component("pdfium") { } } +if (pdf_enable_freetype) { + group("freetype_common") { + public_deps = [] + if (pdf_bundle_freetype) { + public_deps += [ "third_party:fx_freetype" ] + } else { + public_deps += [ "//build/config/freetype" ] + } + } +} + # Targets below this are only visible within this file. visibility = [ ":*" ] @@ -371,6 +382,8 @@ test("pdfium_unittests") { "core/fxcrt:unittests", "core/fxge:unittests", "fpdfsdk:unittests", + "testing:test_environments", + "testing:test_support", "testing:unit_test_support", "testing:unittests", "testing/png_codec:unittests", @@ -434,6 +447,8 @@ test("pdfium_embeddertests") { "fpdfsdk:embeddertests", "fpdfsdk/formfiller:embeddertests", "fpdfsdk/pwl:embeddertests", + "testing:test_environments", + "testing:test_support", "testing/png_codec", "//testing/gmock", "//testing/gtest", diff --git a/DEPS b/DEPS index 081f4bd49e..34ffdf01be 100644 --- a/DEPS +++ b/DEPS @@ -86,11 +86,11 @@ 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': '435e7d977fb36fb47854a4c552c0706dad0bd7cf', + 'abseil_revision': 'a00d74bb55b38f213a3c6110b4bcab5bb565bd56', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_ndk # and whatever else without interference from each other. - 'android_ndk_version': Str('2@30.0.16138531'), + 'android_ndk_version': Str('2@30.0.16248370'), # Three lines of non-changing comments so that # the commit queue can handle CLs rolling brotli # and whatever else without interference from each other. @@ -98,23 +98,23 @@ 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': '2cd94c0abeada712aeea6906d99021913c9fc28d', + 'build_revision': 'ebdaeefa62db7c40f27cacb0eb034db3a5cdb849', # 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': '6f6a5dbf04b734214f3b1f386567d101ec9d607e', + 'buildtools_revision': '64e87f9fbdaf6f53f6cff8f15e4b06f17cead514', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '50c971fc0958d8c1fb0adcc97f41b90042fa7c87', + 'catapult_revision': 'e1165abbf12a3d9ecf2d3151a404c930b5047894', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling clang format # and whatever else without interference from each other. - 'clang_format_revision': '70510081984cfcdb14a15b3e08dfe9776dc7ed37', + 'clang_format_revision': '9f796802e5d633c96dbe97e0b6683d5e62594f9f', # 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': '450d823868eaee61e2b2cb986e19aec287f16d58', + 'clang_revision': '52b22f34fa143721c794752f01fbd9f95e7692f0', # 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. @@ -138,7 +138,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling fast_float # and whatever else without interference from each other. - 'fast_float_revision': '34164f547b7df3f5d794ff67e9f885c36819ebfc', + 'fast_float_revision': 'b0ab987b3dfdde13fa1915f65ef2a5c068d9208c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling fp16 # and whatever else without interference from each other. @@ -146,11 +146,11 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype # and whatever else without interference from each other. - 'freetype_revision': '4800589f76dc62fbe523a74a59e0d4850a5dd7df', + 'freetype_revision': '891f904e2e7b4289f615598cd4b69832f87d2f3a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling GN CIPD package version # and whatever else without interference from each other. - 'gn_version': 'git_revision:150a9d6ba0aa7f407aa4feeabc5f03ce9aa7e04b', + 'gn_version': 'git_revision:841e072bef419ae1116477dc75e8c5a662c0212c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling goldctl CIPD package version # and whatever else without interference from each other. @@ -162,7 +162,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling HarfBuzz # and whatever else without interference from each other. - 'harfbuzz_revision': '886fc1e645388080b72f6d9b06347533a0018045', + 'harfbuzz_revision': 'ff36703b95013ec1d437e6038e1aa7c881275f4e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling highway # and whatever else without interference from each other. @@ -170,7 +170,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling icu # and whatever else without interference from each other. - 'icu_revision': '8cc91d9b6ab9991802fd208ee03a69714fd0251c', + 'icu_revision': '5aa526207171ecadf31597f0980a7b7ad8872f33', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling instrumented_lib # and whatever else without interference from each other. @@ -192,7 +192,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libc++abi # and whatever else without interference from each other. - 'libcxxabi_revision': 'fc1897a2c12aa27e703c3ed48b62eba8abf4ce19', + 'libcxxabi_revision': '09351f6ec00c2b65f2d03585ae6da07098b024dc', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libpng # and whatever else without interference from each other. @@ -200,11 +200,15 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libunwind # and whatever else without interference from each other. - 'libunwind_revision': '45120ee2331193c3650acc9c427ed267fab31d62', + 'libunwind_revision': 'eb1ca4993b534b7d565daee144bbc7f9072b967b', + # 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': '320824188c37e5c28738b9652a0ca8087c934bc9', + 'llvm_libc_revision': '14e51fd2cd7ec21294f18a3a0b2fc5339afea271', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling markupsafe # and whatever else without interference from each other. @@ -220,11 +224,11 @@ 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': '89615a645f0220fb03875111c2d9ae4dea9887d8', + 'partition_allocator_revision': 'dc6144ac93b23232ea8d2b931f661a917ad7d4fd', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling pdfium_tests # and whatever else without interference from each other. - 'pdfium_tests_revision': '6a4136071608f159f45e274fed1b7d5f4272ca8f', + 'pdfium_tests_revision': '86219637fec25a23f5f87252b3bd156ff9ca848e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling result_adapter_revision # and whatever else without interference from each other. @@ -232,7 +236,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': '4dec6f65bcb34c4c289736b0e6fd571b35c6c665', + 'rust_revision': 'b8c38a533c84f01fe194fed455f1d53132b0cf36', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling simdutf # and whatever else without interference from each other. @@ -240,11 +244,11 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling siso # and whatever else without interference from each other. - 'siso_version': 'git_revision:efbbe7f1892211b5e9512576843a3c247b6a6d7c', + 'siso_version': 'git_revision:c574896b272b40aac6e44c228ee0b471ea7e3d7d', # 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': '5549c93c9a1c66137ceed424f08c86ce9988290c', + 'skia_revision': '5b1a7f7c06448e5d5f38f3c9360b18cae315576e', # 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. @@ -252,7 +256,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': 'ead1c91bc31ed99788b7d667d0325455c0fa2568', + 'testing_rust_revision': '9c16e2f4c17e384708c68ecc8596c80df5689b48', # 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. @@ -260,19 +264,19 @@ 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': '54d1148695dbbbfc3e00dfd05aff502acaa602e3', + 'tools_rust_revision': 'e61a63fae23826e5bf5650a1bd7da255ba950577', # 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. - 'tools_win_revision': '13cb6e5d223dc49eadd082d3aef4c2a5b0e4c0a0', + 'tools_win_revision': 'ae34cdd9867c5d2b185fadc9d4a9cc82bb741760', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling v8 # and whatever else without interference from each other. - 'v8_revision': 'df65d5c8f2932045c5af72cd7ecd0c8002fa631e', + 'v8_revision': '0d546234ecf0af2032e958300ef8aeb14bc716c9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling zlib # and whatever else without interference from each other. - 'zlib_revision': '285e94b8fa95ad3b7d16b80798ec8dce6febb8c8', + 'zlib_revision': '456ae730017b9b4bd3371927abc82627fd51feef', } # Only these hosts are allowed for dependencies in this DEPS file. @@ -493,169 +497,179 @@ deps = { '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', 'objects': [ { - 'object_name': 'Linux_x64/clang-android-runtime-library-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '780597375163e78615a307a73ec66346b54c58ea3164aa6fbedf24eae85382fc', - 'size_bytes': 6210856, - 'generation': 1787607008868327, + 'object_name': 'Linux_x64/clang-android-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '9551f7013b0210e4d291bfe09191682796240d93002b4317673c7fc280045e62', + 'size_bytes': 6207356, + 'generation': 1790797384572756, 'condition': 'checkout_android and non_git_source', }, { - 'object_name': 'Linux_x64/clang-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '565f7d980b31411e76d9478731cf3f0ac907a46f274d289ea7f03d9137354053', - 'size_bytes': 56894668, - 'generation': 1787607001504676, + 'object_name': 'Linux_x64/clang-linux-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '4e5a4831e4eec7408c0809d2033032d157fe82dc16b5096d32b6536ed4b8c43b', + 'size_bytes': 3102700, + 'generation': 1790797389651907, + '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, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Linux_x64/clang-tidy-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '4290664a4fa8aa11fd1e6b2d3ff4965d26b41cca1a4950f9835ff19115c2384f', - 'size_bytes': 14877500, - 'generation': 1787607001575317, + 'object_name': 'Linux_x64/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '3815203f9987a08b1bf67555125d95a67c1136de074e6e7dbe11957d70cf784d', + 'size_bytes': 14944572, + 'generation': 1790797381856065, 'condition': 'host_os == "linux" and checkout_clang_tidy and non_git_source', }, { - 'object_name': 'Linux_x64/clangd-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '9db50937915608c466da28ba4166cfa69a5f6bcad346233e47807f532cc3075b', - 'size_bytes': 15049952, - 'generation': 1787607001603946, + 'object_name': 'Linux_x64/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '84ee10cf3a4b4cccf9878c464f33eee756751920187d26dfc46481f195b0c177', + 'size_bytes': 15081268, + 'generation': 1790797381894014, 'condition': 'host_os == "linux" and checkout_clangd and non_git_source', }, { - 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '34747a8e113c399daa36f75a5a0a4c0963871ceafbce7c49a3b5133f81a63bae', - 'size_bytes': 2352288, - 'generation': 1787607001865886, + 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': 'f9990e7dc5a3630c8a962726fac172eeff5c6012e027eb342e9df8bc702198c8', + 'size_bytes': 2373504, + 'generation': 1790797382242083, 'condition': 'host_os == "linux" and checkout_clang_coverage_tools and non_git_source', }, { - 'object_name': 'Linux_x64/llvmobjdump-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'cc8a621ec8cc3c48471c90cf4ed88dcc56bfb403824eda10f8b55796abaec280', - 'size_bytes': 5912568, - 'generation': 1787607001713199, + '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': 'Mac/clang-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'ba2b707d83f7a19d81b9403b4ebe8e999d41ae35fead00a142f02b67e197fe2f', - 'size_bytes': 56420628, - 'generation': 1787607010939621, + 'object_name': 'Mac/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': 'eb03cb8b944dde55677427322bc5a8c6fa0cad4ceacba4653a42c6b1290fea2f', + 'size_bytes': 53873488, + 'generation': 1790797391417548, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'da06337f54ba4b36c79e0d379557832bf82b9c7e85fe8ef78a5b953567d98de1', - 'size_bytes': 1012348, - 'generation': 1787607018227451, + '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-tidy-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'ae5cbec1362517b6928162fddb9b160bd1c75e8fd90789718393e1cadf1342ae', - 'size_bytes': 14882308, - 'generation': 1787607010909866, + 'object_name': 'Mac/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '5dc210d6e7a1f2d8decba74a475bef9457db132a7ae1f4cf0fc4b91fd008e146', + 'size_bytes': 14224492, + 'generation': 1790797391469624, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy', }, { - 'object_name': 'Mac/clangd-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '73f5de0f007f8c588593d824147963ec770b66d487a707d1c377a06e34952d89', - 'size_bytes': 16779464, - 'generation': 1787607010899287, + 'object_name': 'Mac/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': 'f988b7dc66993f8612ac036b62828bcb29315ec9e6927331bf6a32bf986a5df7', + 'size_bytes': 15131696, + 'generation': 1790797391569653, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd', }, { - 'object_name': 'Mac/llvm-code-coverage-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'c47eb37873d3290cc95ba5f9027337895eeeaa199c3f70e63c5d09b710faf250', - 'size_bytes': 2411184, - 'generation': 1787607011184644, + 'object_name': 'Mac/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': 'acb0db493e23a145f733a871ac868468932be7fb4988fe2b7b9b568e70c45682', + 'size_bytes': 2260736, + 'generation': 1790797391738507, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac/llvmobjdump-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '4ddefa96f6aaa04ac502610fc736528f65dc3e1b997fc4a5380081937f6ffb1a', - 'size_bytes': 5874672, - 'generation': 1787607011035157, + 'object_name': 'Mac/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '85b912bf5182b5c7092ce92cdc58a35b5e3d04fa9a49514aa51b28bec4c277fc', + 'size_bytes': 5524828, + 'generation': 1790797391756333, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/clang-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '264d803970f9b58ddbdc90464ad82e54cef642c58ec371ba5356dfa7342ece4d', - 'size_bytes': 47226204, - 'generation': 1787607020088085, + 'object_name': 'Mac_arm64/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': 'bbf4ee5fc42bf134aee682d6afaaaddcc64415d20b080e349a45abf6df927070', + 'size_bytes': 44508976, + 'generation': 1790797400949995, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Mac_arm64/clang-tidy-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '3e02eae391e05018fb4e95d3c90b270688a5e3d5f5b31df6c59512e602478f36', - 'size_bytes': 12960456, - 'generation': 1787607020070956, + 'object_name': 'Mac_arm64/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '35b3e2e5eee220259f7908eaf208507aba78f7cfb8dd990c9854558f056ad890', + 'size_bytes': 12220676, + 'generation': 1790797401142057, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy', }, { - 'object_name': 'Mac_arm64/clangd-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'a56514d09cfcb216efa297e5fecd51a2e248faf7db6edd2d444cfe78e0e219d3', - 'size_bytes': 13320572, - 'generation': 1787607020209265, + 'object_name': 'Mac_arm64/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '5dff03b1fdf59ffe374d6c55deb07c65791825737487a7ec2b39d80539144c6b', + 'size_bytes': 12229100, + 'generation': 1790797401064932, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd', }, { - 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'b593c509e8f4269cf4a16272f6eddcea0f9dd47f6f3fe39b9813041d0f96d4e3', - 'size_bytes': 2031112, - 'generation': 1787607020489366, + 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '1d92f7504529998f971fffbb2dd6214273ed3e6bcfe66e5ca9c1227856df5b4b', + 'size_bytes': 1861564, + 'generation': 1790797401395439, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': 'c67f5dba316d6367be8c153ede456fdbef0dfffad4da10aaf597ff967c07378c', - 'size_bytes': 5610096, - 'generation': 1787607020228775, + 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '0db8f38d5928777104b9d04fc4bcf9c19c17ea252a0745ca038565e3094be9d4', + 'size_bytes': 5235388, + 'generation': 1790797401139458, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/clang-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '166437ede456b3d7cf0d9317287511e47714f91da547dbde02992261aeef7174', - 'size_bytes': 51467544, - 'generation': 1787607030157700, + 'object_name': 'Win/clang-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '86ac9f61c4350f17d7571e402d5fa5f2367015d72ea8efbc4e1e52e843c74fb0', + 'size_bytes': 51822620, + 'generation': 1790797410692058, 'condition': 'host_os == "win"', }, { - 'object_name': 'Win/clang-tidy-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '13cae70dfa207c9ec27e294f38b732b5c820ef637f48d24cc8b97f5f3a96e220', - 'size_bytes': 15134360, - 'generation': 1787607030186388, + 'object_name': 'Win/clang-tidy-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '7d428d76bf66dd603860270ba0a41e88bbbb66c32770aae541c3a6777a9eb57f', + 'size_bytes': 15133896, + 'generation': 1790797410629185, 'condition': 'host_os == "win" and checkout_clang_tidy', }, { - 'object_name': 'Win/clang-win-runtime-library-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '4ff9a06f5fc4a711103cd890e724ae4448d67873de438542f3ac6772e68d3c63', - 'size_bytes': 2638196, - 'generation': 1787607037249082, + 'object_name': 'Win/clang-win-runtime-library-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '0ea5939bf24f293c63621b81d90a88284b6d6563a51be1304b73d2cf92537ffc', + 'size_bytes': 2652404, + 'generation': 1790797418368249, 'condition': 'checkout_win and not host_os == "win"', }, { - 'object_name': 'Win/clangd-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '17f1d575127724051099a778d77e609f650702248cf4e7ff3ec33b377053edd9', - 'size_bytes': 15444040, - 'generation': 1787607030301504, + 'object_name': 'Win/clangd-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '70955f11ea7a22b25807d00b97351c3853d8a9b1e8d1d08c165bc015320858c1', + 'size_bytes': 15434728, + 'generation': 1790797563126146, 'condition': 'host_os == "win" and checkout_clangd', }, { - 'object_name': 'Win/llvm-code-coverage-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '78aa6b2b9cf057077be6705b76e43ffed83e46e17ee86da1ab699340129b1aca', - 'size_bytes': 2512968, - 'generation': 1787607030493491, + 'object_name': 'Win/llvm-code-coverage-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': 'b07c06d983817b4f98878bcc264825835efbd9e5548a18a7d6dfb7623b9c4024', + 'size_bytes': 2527180, + 'generation': 1790797410912461, 'condition': 'host_os == "win" and checkout_clang_coverage_tools', }, { - 'object_name': 'Win/llvmobjdump-llvmorg-24-init-3796-g20e97c4b-4.tar.xz', - 'sha256sum': '4b899193da883a447c75f057e3a94f5e7ab93e5df5724ad94cbd6bf82ea792b4', - 'size_bytes': 5994644, - 'generation': 1787607030276791, + 'object_name': 'Win/llvmobjdump-llvmorg-24-init-7747-g62397f8b-57.tar.xz', + 'sha256sum': '7b34bdb18311a40a234c19860b2c289f6cd7ce16f12d3a449dcf6db6dff043eb', + 'size_bytes': 6010064, + 'generation': 1790797410754258, 'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"', }, ] @@ -665,6 +679,42 @@ deps = { Var('chromium_git') + '/external/github.com/llvm/llvm-project/libc.git@' + Var('llvm_libc_revision'), + 'third_party/llvm-libclang': { + 'dep_type': 'gcs', + '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, + '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, + '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, + '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, + 'condition': 'host_os == "win"', + }, + ], + 'condition': 'checkout_v8', + }, + 'third_party/markupsafe': { 'url': Var('chromium_git') + '/chromium/src/third_party/markupsafe.git@' + Var('markupsafe_revision'), @@ -697,31 +747,31 @@ deps = { 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/rust-toolchain-0913b18e489ac1011b580e31fa5559654be12bfc-2-llvmorg-24-init-3796-g20e97c4b.tar.xz', - 'sha256sum': '4b131e81d157a97bcf454ad0fbb71bc2063b2a3708c858410c04201ef06134e5', - 'size_bytes': 276314860, - 'generation': 1786821088882867, + 'object_name': 'Linux_x64/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', + 'sha256sum': '1f5b945345630c2d5c91a53fd506af476d617b79f251c7ccd2bb707a1f0faa92', + 'size_bytes': 275879080, + 'generation': 1790798148502695, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Mac/rust-toolchain-0913b18e489ac1011b580e31fa5559654be12bfc-2-llvmorg-24-init-3796-g20e97c4b.tar.xz', - 'sha256sum': '12ca849195c27495073ba52ae7126d8a4a9a5807cd7a0dc6ba6b6fa612302a6d', - 'size_bytes': 264031896, - 'generation': 1786821092536805, + 'object_name': 'Mac/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', + 'sha256sum': 'd2ca8d43ebd3e5ce956cfa9e1e7a292cf3faedd0113550a047d08a5cff5d10bf', + 'size_bytes': 263208960, + 'generation': 1790798152115147, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/rust-toolchain-0913b18e489ac1011b580e31fa5559654be12bfc-2-llvmorg-24-init-3796-g20e97c4b.tar.xz', - 'sha256sum': 'fe128475561d7a51c797d1cb3ccea6f94fd770b167c8e0cc33bf75e7c25d8225', - 'size_bytes': 248465344, - 'generation': 1786821096222521, + 'object_name': 'Mac_arm64/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', + 'sha256sum': '68b2b16c3bf32a141fa6ba8e3c51887d6c8be44659b7b8f188d9af16543bf869', + 'size_bytes': 247890912, + 'generation': 1790798155770959, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/rust-toolchain-0913b18e489ac1011b580e31fa5559654be12bfc-2-llvmorg-24-init-3796-g20e97c4b.tar.xz', - 'sha256sum': '14bc9cea5e00cb191f58204ef44d68a6794f856a76f885c50298a12d052035bc', - 'size_bytes': 414479372, - 'generation': 1786821099612317, + 'object_name': 'Win/rust-toolchain-1edd55dcfcd573872c727fa3e086369a71661ee0-1110-llvmorg-24-init-7747-g62397f8b.tar.xz', + 'sha256sum': '630ccbd409fcef327cba0044bf72012f4023fab890a8db26c9731a03d5bdcb8e', + 'size_bytes': 416254844, + 'generation': 1790798159238637, 'condition': 'host_os == "win"', }, ], diff --git a/PRESUBMIT.py b/PRESUBMIT.py index f430bf7cc8..611e3ae68b 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -433,6 +433,27 @@ def _CheckLibcxxRevision(input_api, output_api): return [] +def _CheckLibclangRevision(input_api, output_api): + """Makes sure that third_party/llvm-libclang and third_party/rust-toolchain + are in sync in DEPS. + """ + if 'DEPS' not in [f.LocalPath() for f in input_api.AffectedFiles()]: + return [] + + script_path = input_api.os_path.join('testing', 'tools', 'libclang_check.py') + + try: + errors = input_api.subprocess.check_output([script_path, 'DEPS']) + except input_api.subprocess.CalledProcessError as error: + msg = 'libclang_check.py failed:' + long_text = error.output.decode('utf-8', 'ignore') + return [output_api.PresubmitError(msg, long_text=long_text)] + + if errors: + return [output_api.PresubmitError(errors)] + return [] + + def _CheckTestDuplicates(input_api, output_api): """Checks that pixel and javascript tests don't contain duplicates. We use .in and .pdf files, having both can cause race conditions on the bots, @@ -599,6 +620,7 @@ def CheckChangeOnUpload(input_api, output_api): input_api, output_api, lint_filters=LINT_FILTERS)) results.extend(_CheckIncludeOrder(input_api, output_api)) results.extend(_CheckLibcxxRevision(input_api, output_api)) + results.extend(_CheckLibclangRevision(input_api, output_api)) results.extend(_CheckTestDuplicates(input_api, output_api)) results.extend(_CheckPngNames(input_api, output_api)) results.extend(_CheckUselessForwardDeclarations(input_api, output_api)) diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni index cdbbca4b53..e89d55372a 100644 --- a/build_overrides/pdfium.gni +++ b/build_overrides/pdfium.gni @@ -9,6 +9,10 @@ # Default: Use a bundled FreeType. pdf_bundle_freetype_override = true +# Build PDFium with FreeType support. +# Default: With FreeType support. +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 diff --git a/core/fdrm/BUILD.gn b/core/fdrm/BUILD.gn index 39a93c4f83..159bce5ce0 100644 --- a/core/fdrm/BUILD.gn +++ b/core/fdrm/BUILD.gn @@ -18,12 +18,16 @@ source_set("fdrm") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - deps = [ "../fxcrt" ] + visibility = [ "../../*" ] + public_deps = [ "../fxcrt" ] } pdfium_unittest_source_set("unittests") { sources = [ "fx_crypt_unittest.cpp" ] - deps = [ ":fdrm" ] + deps = [ + ":fdrm", + "../../testing:test_support", + ] pdfium_root_dir = "../../" } diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn index 3a0d7e8927..d60502b49d 100644 --- a/core/fpdfapi/edit/BUILD.gn +++ b/core/fpdfapi/edit/BUILD.gn @@ -50,12 +50,15 @@ source_set("edit") { "../../../:pdfium_strict_config", "../../../:pdfium_noshorten_config", ] + public_deps = [ + "../../fxcrt", + "../parser", + ] deps = [ "../../../constants", - "../../fxcrt", + "../../fxge", "../font", "../page", - "../parser", "//third_party/harfbuzz", ] if (pdf_use_skia) { @@ -102,6 +105,8 @@ pdfium_embeddertest_source_set("embeddertests") { ] deps = [ ":edit", + "../../../testing:path_service", + "../../../testing:test_support", "../../fxge", "../font", "../parser", diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp index c9a6744342..b14d525b81 100644 --- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp @@ -1369,8 +1369,9 @@ void CPDF_PageContentGenerator::ProcessShading(fxcrt::ostringstream* buf, void CPDF_PageContentGenerator::ProcessPathPoints(fxcrt::ostringstream* buf, CPDF_Path* pPath) { pdfium::span points = pPath->GetPoints(); - if (pPath->IsRect()) { - CFX_PointF diff = points[2].point_ - points[0].point_; + std::optional corner = pPath->GetRectOppositeCorner(); + if (corner.has_value()) { + CFX_PointF diff = corner.value() - points[0].point_; WritePoint(*buf, points[0].point_) << " "; WritePoint(*buf, diff) << " re"; return; diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp index d4aecc60fb..56b9aecf93 100644 --- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp @@ -103,6 +103,36 @@ TEST_F(CPDFPageContentGeneratorTest, ProcessRect) { buf.str(""); TestProcessPath(&generator, &buf, pPathObj.get()); EXPECT_EQ("q 0 0 5.2 3.78 re n Q\n", ByteString(buf)); + + // A rectangle with a duplicate second point, so its third point is not the + // opposite corner. + pPathObj = std::make_unique(); + pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove); + pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(2, 0), CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(2, 1), CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(0, 1), CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kLine); + buf.str(""); + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ("q 0 0 2 1 re n Q\n", ByteString(buf)); + + // A rectangle with an extra point in the middle of its first edge, so its + // third point is not the opposite corner either. + pPathObj = std::make_unique(); + pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove); + pPathObj->path().AppendPoint(CFX_PointF(50, 0), CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(100, 0), + CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(100, 100), + CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(0, 100), + CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPointAndClose(CFX_PointF(0, 0), + CFX_Path::Point::Type::kLine); + buf.str(""); + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ("q 0 0 100 100 re n Q\n", ByteString(buf)); } TEST_F(CPDFPageContentGeneratorTest, Bug937) { diff --git a/core/fpdfapi/font/BUILD.gn b/core/fpdfapi/font/BUILD.gn index 8684cfcd4f..62304889e0 100644 --- a/core/fpdfapi/font/BUILD.gn +++ b/core/fpdfapi/font/BUILD.gn @@ -42,13 +42,13 @@ source_set("font") { "../../../:pdfium_strict_config", "../../../:pdfium_noshorten_config", ] - deps = [ - "../../../constants", + public_deps = [ "../../fxcrt", "../../fxge", "../cmaps", "../parser", ] + deps = [ "../../../constants" ] visibility = [ "../../../*" ] if (is_mac) { frameworks = [ "CoreFoundation.framework" ] diff --git a/core/fpdfapi/font/cpdf_cidfont.cpp b/core/fpdfapi/font/cpdf_cidfont.cpp index a2380c9633..ecf181d289 100644 --- a/core/fpdfapi/font/cpdf_cidfont.cpp +++ b/core/fpdfapi/font/cpdf_cidfont.cpp @@ -720,7 +720,7 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { RetainPtr face = font_.GetFace(); bool bMSUnicode = UseTTCharmapUnicode(face); bool bMacRoman = - !bMSUnicode && UseTTCharmap(face, CFX_Face::kMacRomanCmapId); + !bMSUnicode && UseTTCharmap(face, CFX_Face::kMacRomanCharMapIdPair); FontEncoding base_encoding = FontEncoding::kStandard; if (bMSUnicode) { base_encoding = FontEncoding::kWinAnsi; diff --git a/core/fpdfapi/font/cpdf_font.cpp b/core/fpdfapi/font/cpdf_font.cpp index 53ba73a567..1cf2401094 100644 --- a/core/fpdfapi/font/cpdf_font.cpp +++ b/core/fpdfapi/font/cpdf_font.cpp @@ -600,12 +600,13 @@ bool CPDF_Font::UseTTCharmapUnicode(const RetainPtr& face) { bool charmap_unicode_found = false; bool charmap_mssymbol_found = false; for (size_t i = 0; i < face->GetCharMapCount(); i++) { - const CFX_Face::CharMapId charmap_id = face->GetCharMapIdByIndex(i); - if (charmap_id == CFX_Face::kWindowsUnicodeCmapId) { + const CFX_Face::CharMapIdPair charmap_id_pair = + face->GetCharMapIdPairByIndex(i); + if (charmap_id_pair == CFX_Face::kWindowsUnicodeCharMapIdPair) { face->SetCharMapByIndex(i); return true; } - if (charmap_id == CFX_Face::kWindowsSymbolCmapId) { + if (charmap_id_pair == CFX_Face::kWindowsSymbolCharMapIdPair) { charmap_mssymbol_found = true; continue; } @@ -624,9 +625,9 @@ bool CPDF_Font::UseTTCharmapUnicode(const RetainPtr& face) { // static bool CPDF_Font::UseTTCharmap(const RetainPtr& face, - const CFX_Face::CharMapId& cmap_id) { + const CFX_Face::CharMapIdPair& charmap_id_pair) { for (size_t i = 0; i < face->GetCharMapCount(); i++) { - if (face->GetCharMapIdByIndex(i) == cmap_id) { + if (face->GetCharMapIdPairByIndex(i) == charmap_id_pair) { face->SetCharMapByIndex(i); return true; } diff --git a/core/fpdfapi/font/cpdf_font.h b/core/fpdfapi/font/cpdf_font.h index b5dff86423..049c42eb25 100644 --- a/core/fpdfapi/font/cpdf_font.h +++ b/core/fpdfapi/font/cpdf_font.h @@ -164,7 +164,7 @@ class CPDF_Font : public Retainable, public Observable { static bool UseTTCharmapUnicode(const RetainPtr& face); static bool UseTTCharmap(const RetainPtr& face, - const CFX_Face::CharMapId& cmap_id); + const CFX_Face::CharMapIdPair& charmap_id_pair); static const char* GetAdobeCharName(FontEncoding base_encoding, const std::vector& charnames, diff --git a/core/fpdfapi/font/cpdf_truetypefont.cpp b/core/fpdfapi/font/cpdf_truetypefont.cpp index a423af3a76..861887901a 100644 --- a/core/fpdfapi/font/cpdf_truetypefont.cpp +++ b/core/fpdfapi/font/cpdf_truetypefont.cpp @@ -112,7 +112,7 @@ void CPDF_TrueTypeFont::LoadGlyphMap() { } return; } - if (UseTTCharmap(face, CFX_Face::kWindowsSymbolCmapId)) { + if (UseTTCharmap(face, CFX_Face::kWindowsSymbolCharMapIdPair)) { for (uint32_t charcode = 0; charcode < 256; charcode++) { glyph_index_[charcode] = GetGlyphIndexForMSSymbol(face, charcode); } @@ -125,7 +125,7 @@ void CPDF_TrueTypeFont::LoadGlyphMap() { encoding_.SetUnicode(charcode, UnicodeFromAdobeName(name)); } } - } else if (UseTTCharmap(face, CFX_Face::kMacRomanCmapId)) { + } else if (UseTTCharmap(face, CFX_Face::kMacRomanCharMapIdPair)) { for (uint32_t charcode = 0; charcode < 256; charcode++) { encoding_.SetUnicode(charcode, UnicodeFromAppleRomanCharCode(charcode)); @@ -134,7 +134,7 @@ void CPDF_TrueTypeFont::LoadGlyphMap() { return; } } - if (UseTTCharmap(face, CFX_Face::kMacRomanCmapId)) { + if (UseTTCharmap(face, CFX_Face::kMacRomanCharMapIdPair)) { for (uint32_t charcode = 0; charcode < 256; charcode++) { glyph_index_[charcode] = face->GetCharIndex(charcode); encoding_.SetUnicode(charcode, UnicodeFromAppleRomanCharCode(charcode)); @@ -185,17 +185,17 @@ CPDF_TrueTypeFont::CharmapType CPDF_TrueTypeFont::DetermineCharmapType() const { } if (FontStyleIsNonSymbolic(flags_)) { - if (UseTTCharmap(font_.GetFace(), CFX_Face::kMacRomanCmapId)) { + if (UseTTCharmap(font_.GetFace(), CFX_Face::kMacRomanCharMapIdPair)) { return CharmapType::kMacRoman; } - if (UseTTCharmap(font_.GetFace(), CFX_Face::kWindowsSymbolCmapId)) { + if (UseTTCharmap(font_.GetFace(), CFX_Face::kWindowsSymbolCharMapIdPair)) { return CharmapType::kMSSymbol; } } else { - if (UseTTCharmap(font_.GetFace(), CFX_Face::kWindowsSymbolCmapId)) { + if (UseTTCharmap(font_.GetFace(), CFX_Face::kWindowsSymbolCharMapIdPair)) { return CharmapType::kMSSymbol; } - if (UseTTCharmap(font_.GetFace(), CFX_Face::kMacRomanCmapId)) { + if (UseTTCharmap(font_.GetFace(), CFX_Face::kMacRomanCharMapIdPair)) { return CharmapType::kMacRoman; } } @@ -218,11 +218,11 @@ FontEncoding CPDF_TrueTypeFont::DetermineEncoding() const { bool support_win = false; bool support_mac = false; for (size_t i = 0; i < num_charmaps; i++) { - int platform_id = face->GetCharMapPlatformIdByIndex(i); - if (platform_id == kNamePlatformAppleUnicode || - platform_id == kNamePlatformWindows) { + uint16_t platform_id = face->GetCharMapPlatformIdByIndex(i); + if (platform_id == kPlatformAppleUnicode || + platform_id == kPlatformWindows) { support_win = true; - } else if (platform_id == kNamePlatformMac) { + } else if (platform_id == kPlatformMac) { support_mac = true; } if (support_win && support_mac) { diff --git a/core/fpdfapi/font/cpdf_type1font.cpp b/core/fpdfapi/font/cpdf_type1font.cpp index ff0bda7edb..b4e6acf215 100644 --- a/core/fpdfapi/font/cpdf_type1font.cpp +++ b/core/fpdfapi/font/cpdf_type1font.cpp @@ -153,7 +153,7 @@ void CPDF_Type1Font::LoadGlyphMap() { } #endif if (!IsEmbedded() && !IsSymbolicFont() && font_.IsTTFont()) { - if (UseTTCharmap(face, CFX_Face::kWindowsSymbolCmapId)) { + if (UseTTCharmap(face, CFX_Face::kWindowsSymbolCharMapIdPair)) { bool bGotOne = false; for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) { static constexpr std::array prefix = { diff --git a/core/fpdfapi/page/BUILD.gn b/core/fpdfapi/page/BUILD.gn index 208451a680..95010d53b8 100644 --- a/core/fpdfapi/page/BUILD.gn +++ b/core/fpdfapi/page/BUILD.gn @@ -123,15 +123,14 @@ source_set("page") { ] configs += [ "../../../:pdfium_strict_config" ] public_deps = [ - "../../fxge", - "../parser", - ] - deps = [ "../../../constants", "../../fxcodec", "../../fxcrt", + "../../fxge", "../font", + "../parser", ] + deps = [ "//third_party/abseil-cpp:absl" ] allow_circular_includes_from = [] if (pdf_use_skia) { allow_circular_includes_from += [ "../../fxge" ] @@ -146,10 +145,8 @@ source_set("unit_test_support") { "test_with_page_module.h", ] configs += [ "../../../:pdfium_strict_config" ] - deps = [ - "../page", - "//testing/gtest", - ] + public_deps = [ "//testing/gtest" ] + deps = [ "../page" ] } pdfium_unittest_source_set("unittests") { @@ -170,6 +167,7 @@ pdfium_unittest_source_set("unittests") { ":page", ":unit_test_support", "../../../constants", + "../../../testing:path_service", "../parser", "../parser:unit_test_support", "../render", diff --git a/core/fpdfapi/page/DEPS b/core/fpdfapi/page/DEPS index 3dc6946b70..5ef96e7e7f 100644 --- a/core/fpdfapi/page/DEPS +++ b/core/fpdfapi/page/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - 'cpdf_pageimagecache\.cpp': [ + 'cpdf_pageimagecache\\.cpp': [ '+third_party/skia/include', ], } diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp index 5968217f51..3c7e87466e 100644 --- a/core/fpdfapi/page/cpdf_dib.cpp +++ b/core/fpdfapi/page/cpdf_dib.cpp @@ -31,6 +31,7 @@ #include "core/fpdfapi/parser/fpdf_parser_decode.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" #include "core/fxcodec/basic/basicmodule.h" +#include "core/fxcodec/fx_codec.h" #include "core/fxcodec/icc/icc_transform.h" #include "core/fxcodec/jbig2/jbig2_decoder.h" #include "core/fxcodec/jpeg/jpegmodule.h" @@ -38,7 +39,6 @@ #include "core/fxcodec/scanlinedecoder.h" #include "core/fxcrt/check.h" #include "core/fxcrt/check_op.h" -#include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/data_vector.h" #include "core/fxcrt/fx_2d_size.h" #include "core/fxcrt/fx_safe_types.h" @@ -71,6 +71,18 @@ unsigned int GetBits8(pdfium::span pData, return (byte >> (8 - nbits - (bitpos % 8))) & ((1 << nbits) - 1); } +// A 16 bits-per-component RGB source pixel. PDF stores the samples +// big-endian, so the first byte of each component is its high byte; the +// translation below keeps only that byte. +struct RgbBe16 { + uint8_t red_high; + uint8_t red_low; + uint8_t green_high; + uint8_t green_low; + uint8_t blue_high; + uint8_t blue_low; +}; + bool GetBitValue(pdfium::span pSrc, size_t pos) { return pSrc[pos / 8] & (1 << (7 - pos % 8)); } @@ -1077,7 +1089,8 @@ void CPDF_DIB::TranslateScanline24bpp( uint64_t src_byte_pos = 0; size_t dest_byte_pos = 0; const bool bpp8 = bpc_ == 8; - for (int column = 0; column < GetWidth(); column++) { + const int width = GetWidth(); + for (int column = 0; column < width; column++) { for (uint32_t color = 0; color < components_; color++) { if (bpp8) { uint8_t data = src_scan[src_byte_pos++]; @@ -1132,34 +1145,40 @@ bool CPDF_DIB::TranslateScanline24bppDefaultDecode( return true; } - uint8_t* dest_pos = dest_scan.data(); - const uint8_t* src_pos = src_scan.data(); + const size_t width = pdfium::checked_cast(GetWidth()); + auto dest_pixels = + fxcrt::reinterpret_span>(dest_scan).first(width); switch (bpc_) { case 8: - UNSAFE_TODO({ - for (int column = 0; column < GetWidth(); column++) { - *dest_pos++ = src_pos[2]; - *dest_pos++ = src_pos[1]; - *dest_pos++ = *src_pos; - src_pos += 3; - } - }); + // The whole translation is the per-pixel RGB-to-BGR byte swap; + // ReverseRGB() is that swap, in a form the compiler vectorizes + // (the destination scanline buffer never overlaps the source). + fxcodec::ReverseRGB(dest_scan, src_scan, GetWidth()); break; - case 16: - UNSAFE_TODO({ - for (int col = 0; col < GetWidth(); col++) { - *dest_pos++ = src_pos[4]; - *dest_pos++ = src_pos[2]; - *dest_pos++ = *src_pos; - src_pos += 6; - } - }); + case 16: { + // Keep each component's high byte, in BGR order. + auto src_pixels = + fxcrt::reinterpret_span(src_scan).first(width); + for (auto [src, dest] : fxcrt::Zip(src_pixels, dest_pixels)) { + // Compiler can't conclude src/dest don't overlap. + const uint8_t blue = src.blue_high; + const uint8_t green = src.green_high; + const uint8_t red = src.red_high; + dest.blue = blue; + dest.green = green; + dest.red = red; + } break; - default: + } + default: { + // Fewer than 8 bits per component: read each component out of the bit + // stream and scale it to 8 bits. GetScanline() sends images with + // bpc * components <= 8 down a different path before reaching here, so + // with the 3 components this function requires, only 4 bits per + // component arrives. const unsigned int max_data = (1 << bpc_) - 1; uint64_t src_bit_pos = 0; - size_t dest_byte_pos = 0; - for (int column = 0; column < GetWidth(); column++) { + for (auto& dest : dest_pixels) { unsigned int R = GetBits8(src_scan, src_bit_pos, bpc_); src_bit_pos += bpc_; unsigned int G = GetBits8(src_scan, src_bit_pos, bpc_); @@ -1169,14 +1188,12 @@ bool CPDF_DIB::TranslateScanline24bppDefaultDecode( R = std::min(R, max_data); G = std::min(G, max_data); B = std::min(B, max_data); - UNSAFE_TODO({ - dest_pos[dest_byte_pos] = B * 255 / max_data; - dest_pos[dest_byte_pos + 1] = G * 255 / max_data; - dest_pos[dest_byte_pos + 2] = R * 255 / max_data; - dest_byte_pos += 3; - }); + dest.blue = B * 255 / max_data; + dest.green = G * 255 / max_data; + dest.red = R * 255 / max_data; } break; + } } return true; } diff --git a/core/fpdfapi/page/cpdf_path.cpp b/core/fpdfapi/page/cpdf_path.cpp index 9a0de96d77..5758e3335e 100644 --- a/core/fpdfapi/page/cpdf_path.cpp +++ b/core/fpdfapi/page/cpdf_path.cpp @@ -37,6 +37,10 @@ bool CPDF_Path::IsRect() const { return ref_.GetObject()->IsRect(); } +std::optional CPDF_Path::GetRectOppositeCorner() const { + return ref_.GetObject()->GetRectOppositeCorner(); +} + void CPDF_Path::Transform(const CFX_Matrix& matrix) { ref_.GetPrivateCopy()->Transform(matrix); } diff --git a/core/fpdfapi/page/cpdf_path.h b/core/fpdfapi/page/cpdf_path.h index 104c136e43..930a4c9cc7 100644 --- a/core/fpdfapi/page/cpdf_path.h +++ b/core/fpdfapi/page/cpdf_path.h @@ -7,6 +7,7 @@ #ifndef CORE_FPDFAPI_PAGE_CPDF_PATH_H_ #define CORE_FPDFAPI_PAGE_CPDF_PATH_H_ +#include #include #include "core/fxcrt/shared_copy_on_write.h" @@ -30,6 +31,7 @@ class CPDF_Path { float miter_limit) const; bool IsRect() const; + std::optional GetRectOppositeCorner() const; void Transform(const CFX_Matrix& matrix); void Append(const CFX_Path& path, const CFX_Matrix* pMatrix); diff --git a/core/fpdfapi/parser/BUILD.gn b/core/fpdfapi/parser/BUILD.gn index d73c1e737b..1dc29d54a3 100644 --- a/core/fpdfapi/parser/BUILD.gn +++ b/core/fpdfapi/parser/BUILD.gn @@ -101,15 +101,22 @@ source_set("parser") { "object_tree_traversal_util.h", ] configs += [ "../../../:pdfium_strict_config" ] - deps = [ - "../../../constants", + public_deps = [ "../../fdrm", "../../fxcodec", + "../../fxcrt", ] - public_deps = [ "../../fxcrt" ] + deps = [ "../../../constants" ] allow_circular_includes_from = [] visibility = [ "../../../*" ] + # EmbedPDF: the base and layer documents make their page and render data + # (cpdf_docpagedata.h, cpdf_docrenderdata.h), whose font headers need + # FreeType's include path; fxge passes it on (fxcrt no longer does). + if (!pdf_use_skia) { + deps += [ "../../fxge" ] + } + if (pdf_enable_xfa) { sources += [ "cpdf_seekablemultistream.cpp", @@ -129,8 +136,11 @@ source_set("unit_test_support") { "cpdf_test_document.h", ] configs += [ "../../../:pdfium_strict_config" ] - deps = [ + public_deps = [ ":parser", + "../../fxcrt", + ] + deps = [ "../page", "../render", ] @@ -168,6 +178,8 @@ pdfium_unittest_source_set("unittests") { ":parser", ":unit_test_support", "../../../constants", + "../../../testing:path_service", + "../../../testing:test_support", "../../fxcodec", "../page", "../page:unit_test_support", @@ -189,6 +201,7 @@ pdfium_embeddertest_source_set("embeddertests") { ] deps = [ ":parser", + "../../../testing:test_support", "../../fxge", ] pdfium_root_dir = "../../../" diff --git a/core/fpdfapi/render/BUILD.gn b/core/fpdfapi/render/BUILD.gn index 46cbf0d363..6cad790896 100644 --- a/core/fpdfapi/render/BUILD.gn +++ b/core/fpdfapi/render/BUILD.gn @@ -38,15 +38,17 @@ source_set("render") { "../../../:pdfium_strict_config", "../../../:pdfium_noshorten_config", ] - deps = [ - "../../../constants", - "../../fxcodec", + public_deps = [ "../../fxcrt", "../../fxge", - "../font", "../page", "../parser", ] + deps = [ + "../../../constants", + "../../fxcodec", + "../font", + ] visibility = [ "../../../*" ] if (is_win) { sources += [ diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp index 6942aa3763..5d1e4fa8fa 100644 --- a/core/fpdfapi/render/cpdf_imagerenderer.cpp +++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp @@ -335,10 +335,12 @@ bool CPDF_ImageRenderer::DrawPatternImage() { return false; } - CFX_Matrix new_matrix = GetDrawMatrix(rect); + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::kBgra)) { + return true; + } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForNewBitmap(rect.Width(), rect.Height(), - FXDIB_Format::kBgra); + CFX_RenderDevice::CreateForBitmap(bitmap); if (!bitmap_device) { return true; } @@ -361,15 +363,16 @@ bool CPDF_ImageRenderer::DrawPatternImage() { pattern_matrix, false); } + const CFX_Matrix new_matrix = GetDrawMatrix(rect); RetainPtr mask_bitmap = CalculateDrawImage(bitmap_device.get(), dibbase_, new_matrix, rect); if (!mask_bitmap) { return true; } - bitmap_device->GetBitmap()->MultiplyAlphaMask(std::move(mask_bitmap)); + bitmap->MultiplyAlphaMask(std::move(mask_bitmap)); render_status_->GetRenderDevice()->SetDIBitsWithBlend( - bitmap_device->GetBitmap(), rect.left, rect.top, blend_type_); + std::move(bitmap), rect.left, rect.top, blend_type_); return false; } @@ -386,14 +389,18 @@ bool CPDF_ImageRenderer::DrawMaskedImage() { return false; } - CFX_Matrix new_matrix = GetDrawMatrix(rect); + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::kBgrx)) { + return true; + } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForNewBitmap(rect.Width(), rect.Height(), - FXDIB_Format::kBgrx); + CFX_RenderDevice::CreateForBitmap(bitmap); if (!bitmap_device) { return true; } bitmap_device->Clear(0xffffffff); + + const CFX_Matrix new_matrix = GetDrawMatrix(rect); CPDF_RenderStatus bitmap_status(render_status_->GetContext(), bitmap_device.get()); bitmap_status.SetDropObjects(render_status_->GetDropObjects()); @@ -412,15 +419,14 @@ bool CPDF_ImageRenderer::DrawMaskedImage() { #if defined(PDF_USE_SKIA) if (CFX_GEModule::IsSkiaRenderer() && render_status_->GetRenderDevice()->SetBitsWithMask( - bitmap_device->GetBitmap(), mask_bitmap, rect.left, rect.top, alpha_, - blend_type_)) { + bitmap, mask_bitmap, rect.left, rect.top, alpha_, blend_type_)) { return false; } #endif - bitmap_device->GetBitmap()->MultiplyAlphaMask(std::move(mask_bitmap)); - bitmap_device->GetBitmap()->MultiplyAlpha(alpha_); + bitmap->MultiplyAlphaMask(std::move(mask_bitmap)); + bitmap->MultiplyAlpha(alpha_); render_status_->GetRenderDevice()->SetDIBitsWithBlend( - bitmap_device->GetBitmap(), rect.left, rect.top, blend_type_); + std::move(bitmap), rect.left, rect.top, blend_type_); return false; } diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp index 2aa56af5c0..5753aa5b33 100644 --- a/core/fpdfapi/render/cpdf_renderstatus.cpp +++ b/core/fpdfapi/render/cpdf_renderstatus.cpp @@ -708,9 +708,12 @@ bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj, } device_->GetDIBits(backdrop, rect.left, rect.top); } + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(width, height, GetCompatibleArgbFormat())) { + return true; + } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForNewBitmapWithBackdrop( - width, height, GetCompatibleArgbFormat(), std::move(backdrop)); + CFX_RenderDevice::CreateForBitmap(bitmap, std::move(backdrop)); if (!bitmap_device) { return true; } @@ -1055,9 +1058,12 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, // TODO(crbug.com/42271020): Consider adding support for // `FXDIB_Format::kBgraPremul` + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::kBgra)) { + return true; + } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForNewBitmap(rect.Width(), rect.Height(), - FXDIB_Format::kBgra); + CFX_RenderDevice::CreateForBitmap(bitmap); if (!bitmap_device) { return true; } @@ -1492,8 +1498,12 @@ RetainPtr CPDF_RenderStatus::LoadSMask( const int width = clip_rect.Width(); const int height = clip_rect.Height(); const FXDIB_Format format = GetFormatForLuminosity(bLuminosity); + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(width, height, format)) { + return nullptr; + } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForNewBitmap(width, height, format); + CFX_RenderDevice::CreateForBitmap(bitmap); if (!bitmap_device) { return nullptr; } @@ -1526,7 +1536,6 @@ RetainPtr CPDF_RenderStatus::LoadSMask( } pdfium::span dest_buf = result_mask->GetWritableBuffer(); - RetainPtr bitmap = bitmap_device->GetBitmap(); pdfium::span src_buf = bitmap->GetBuffer(); const int dest_pitch = result_mask->GetPitch(); const int src_pitch = bitmap->GetPitch(); diff --git a/core/fpdfapi/render/cpdf_rendertiling.cpp b/core/fpdfapi/render/cpdf_rendertiling.cpp index f01364ca06..94ca355890 100644 --- a/core/fpdfapi/render/cpdf_rendertiling.cpp +++ b/core/fpdfapi/render/cpdf_rendertiling.cpp @@ -54,11 +54,11 @@ RetainPtr CPDF_RenderTiling::DrawPatternBitmap( return nullptr; } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForBitmapWithBackdropAndGroupKnockout( - pBitmap, /*backdrop_bitmap=*/nullptr, /*group_knockout=*/true); + CFX_RenderDevice::CreateForBitmap(pBitmap); if (!bitmap_device) { return nullptr; } + bitmap_device->SetGroupKnockout(true); CFX_FloatRect cell_bbox = pPattern->pattern_to_form().TransformRect(pPattern->bbox()); cell_bbox = mtObject2Device.TransformRect(cell_bbox); diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp index dd9f770862..6203a0e718 100644 --- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp +++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp @@ -6,6 +6,8 @@ #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h" +#include + #include "build/build_config.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/render/cpdf_devicebuffer.h" @@ -38,8 +40,9 @@ bool CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* context, matrix_.TransformRect(CFX_FloatRect(rect_)).GetOuterRect(); int32_t width = bitmap_rect.Width(); int32_t height = bitmap_rect.Height(); + // Set to 0 to make CalculatePitchAndSize() calculate it. - static constexpr uint32_t kNoPitch = 0; + constexpr uint32_t kNoPitch = 0; std::optional pitch_size = CFX_DIBitmap::CalculatePitchAndSize(width, height, dibFormat, kNoPitch); if (!pitch_size.has_value()) { @@ -47,14 +50,21 @@ bool CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* context, } if (pitch_size.value().size <= kImageSizeLimitBytes) { - bitmap_device_ = - CFX_RenderDevice::CreateForNewBitmap(width, height, dibFormat); - if (bitmap_device_) { - context->GetBackgroundToDevice(bitmap_device_.get(), pObj, &options, - matrix_); - return true; + auto bitmap = pdfium::MakeRetain(); + if (bitmap->Create(width, height, dibFormat)) { + bitmap_device_ = CFX_RenderDevice::CreateForBitmap(std::move(bitmap)); + if (bitmap_device_) { + context->GetBackgroundToDevice(bitmap_device_.get(), pObj, &options, + matrix_); + return true; + } } } + + // Once dimensions are at a minimum, further scaling cannot help. + if (width <= 1 && height <= 1) { + return false; + } matrix_.Scale(0.5f, 0.5f); } } diff --git a/core/fpdfdoc/BUILD.gn b/core/fpdfdoc/BUILD.gn index 879a7690a4..24fd87c902 100644 --- a/core/fpdfdoc/BUILD.gn +++ b/core/fpdfdoc/BUILD.gn @@ -124,10 +124,8 @@ source_set("fpdfdoc") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - deps = [ + public_deps = [ "../../constants", - "../fpdfapi/font", - "../fpdfapi/page", "../fpdfapi/parser", "../fpdfapi/render", "../fxcrt", @@ -136,6 +134,11 @@ source_set("fpdfdoc") { # glyphs used by each saved annotation/layer. "../../third_party/harfbuzz", ] + deps = [ + "../fpdfapi/font", + "../fpdfapi/page", + "//third_party/abseil-cpp:absl", + ] visibility = [ "../../*" ] } @@ -165,6 +168,7 @@ pdfium_unittest_source_set("unittests") { deps = [ ":fpdfdoc", "../../constants", + "../../testing:test_support", "../fpdfapi/font", "../fpdfapi/page", "../fpdfapi/page:unit_test_support", diff --git a/core/fpdfdoc/cpdf_annot.cpp b/core/fpdfdoc/cpdf_annot.cpp index aa2cae3ecc..55446c64c4 100644 --- a/core/fpdfdoc/cpdf_annot.cpp +++ b/core/fpdfdoc/cpdf_annot.cpp @@ -167,7 +167,7 @@ CPDF_Annot::CPDF_Annot(RetainPtr dict, annot_dict_->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false) || (CanGenerateEphemeralAP() && ShouldGenerateAP())) { // EmbedPDF(reads-dont-write): construction writes no appearance for a kind - // drawn from memory, or for a popup PDFium makes. + // drawn from memory. // Drawing generates a missing appearance too: see DrawAppearance() and // DrawInContext(). if (missing_appearance == MissingAppearance::kGenerateNow && @@ -203,7 +203,9 @@ bool CPDF_Annot::ShouldGenerateAP() const { return false; } - return !IsHidden(); + // Closed popups are recreated on every render. Generate their appearance + // only when opened; DrawAppearance() and DrawInContext() retry at that point. + return ShouldDrawAnnotation(); } // EmbedPDF(reads-dont-write): a missing appearance is built in memory, not diff --git a/core/fpdfdoc/cpdf_annot.h b/core/fpdfdoc/cpdf_annot.h index e2e67bc268..36139cc888 100644 --- a/core/fpdfdoc/cpdf_annot.h +++ b/core/fpdfdoc/cpdf_annot.h @@ -198,14 +198,13 @@ class CPDF_Annot { size_t nIndex); static size_t QuadPointCount(const CPDF_Array* pArray); - // EmbedPDF(reads-dont-write): a popup PDFium makes for a markup annotation - // gets its appearance only when drawn, so building the annotation list for - // a render writes none for the closed ones. + // EmbedPDF(reads-dont-write): an annotation made to answer a question about + // its drawing, such as where it draws, gets its appearance only when drawn, + // so asking writes nothing. // When an annotation without an appearance gets one generated. enum class MissingAppearance { kGenerateNow, - // Only once it is drawn: for annotations PDFium makes itself, such as the - // popup of a markup annotation, which is drawn only once opened. + // Only once it is drawn. kGenerateWhenDrawn, }; diff --git a/core/fpdfdoc/cpdf_annotlist.cpp b/core/fpdfdoc/cpdf_annotlist.cpp index f1de85d94d..e1517299f1 100644 --- a/core/fpdfdoc/cpdf_annotlist.cpp +++ b/core/fpdfdoc/cpdf_annotlist.cpp @@ -117,15 +117,8 @@ std::unique_ptr CreatePopupAnnot(CPDF_Document* document, pAnnotDict->SetRectFor(pdfium::annotation::kRect, popupRect); pAnnotDict->SetNewFor(pdfium::annotation::kF, 0); - // EmbedPDF(reads-dont-write): a closed popup gets its appearance only when - // drawn. - // The popup is closed, so it is drawn only once opened, and drawing - // generates its appearance. Generating it here would build one, with a font - // and two new objects in the document, for every popup of every list, and - // rendering a page builds a list each time. - auto pPopupAnnot = std::make_unique( - std::move(pAnnotDict), document, - CPDF_Annot::MissingAppearance::kGenerateWhenDrawn); + auto pPopupAnnot = + std::make_unique(std::move(pAnnotDict), document); pAnnot->SetPopupAnnot(pPopupAnnot.get()); return pPopupAnnot; } diff --git a/core/fpdfdoc/cpdf_annotlist_unittest.cpp b/core/fpdfdoc/cpdf_annotlist_unittest.cpp index 2d7260265c..5286059294 100644 --- a/core/fpdfdoc/cpdf_annotlist_unittest.cpp +++ b/core/fpdfdoc/cpdf_annotlist_unittest.cpp @@ -18,11 +18,15 @@ #include "core/fpdfapi/parser/cpdf_read_only_graph_guard.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fpdfapi/parser/cpdf_test_document.h" +#include "core/fpdfapi/render/cpdf_rendercontext.h" #include "core/fpdfdoc/cpdf_annot.h" #include "core/fxcrt/bytestring.h" #include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/retain_ptr.h" #include "core/fxcrt/widestring.h" +#include "core/fxge/cfx_renderdevice.h" +#include "core/fxge/dib/cfx_dibitmap.h" +#include "core/fxge/dib/fx_dib.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -79,6 +83,10 @@ WideString GetDecodedContents(const CPDF_Annot* annotation) { pdfium::annotation::kContents); } +FX_ARGB GetCenterPixel(const CFX_DIBitmap* bitmap) { + return FXARGB_GetDIB(bitmap->GetScanline(100).subspan<400, 4>()); +} + } // namespace TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromPdfEncoded) { @@ -154,3 +162,70 @@ TEST_F(CPDFAnnotListTest, ConstructionPreservesDirectAnnotations) { EXPECT_EQ(annotation.Get(), annots->GetObjectAt(0).Get()); EXPECT_EQ(last_obj_num, document_->GetLastObjNum()); } + +TEST_F(CPDFAnnotListTest, ClosedPopupAppearanceObjects) { + AddTextAnnotation("Regression comment"); + { + CPDF_AnnotList list(page_); + ASSERT_EQ(2u, list.Count()); + // EmbedPDF(reads-dont-write): the text annotation's appearance is drawn + // from memory, so building the list writes none. + EXPECT_FALSE(list.GetAt(0)->GetAnnotDict()->KeyExist("AP")); + EXPECT_FALSE(list.GetAt(1)->GetAnnotDict()->KeyExist("AP")); + } + const uint32_t object_count = document_->GetLastObjNum(); + for (int i = 0; i < 30; ++i) { + CPDF_AnnotList list(page_); + EXPECT_FALSE(list.GetAt(1)->GetAnnotDict()->KeyExist("AP")); + EXPECT_EQ(object_count, document_->GetLastObjNum()); + } +} + +class CPDFPopupRenderTest : public CPDFAnnotListTest, + public testing::WithParamInterface { + protected: + bool UseRenderContext() const { return GetParam(); } + + bool DrawPopup(CPDF_Annot* popup, CFX_RenderDevice* device) { + device->Clear(0xffffffff); + const CFX_Matrix matrix; + if (!UseRenderContext()) { + return popup->DrawAppearance(page_.Get(), device, matrix, + CPDF_Annot::AppearanceMode::kNormal); + } + CPDF_RenderContext context(document_.get(), nullptr, nullptr); + const bool result = popup->DrawInContext( + page_.Get(), &context, matrix, CPDF_Annot::AppearanceMode::kNormal); + context.Render(device, nullptr, nullptr, nullptr); + return result; + } +}; + +TEST_P(CPDFPopupRenderTest, PopupRendering) { + AddTextAnnotation("Regression comment"); + CPDF_AnnotList list(page_); + ASSERT_EQ(2u, list.Count()); + CPDF_Annot* popup = list.GetAt(1); + ASSERT_EQ(CPDF_Annot::Subtype::POPUP, popup->GetSubtype()); + EXPECT_FALSE(popup->GetAnnotDict()->KeyExist("AP")); + auto bitmap = pdfium::MakeRetain(); + ASSERT_TRUE(bitmap->Create(256, 256, FXDIB_Format::kBgra)); + auto device = CFX_RenderDevice::CreateForBitmap(bitmap); + ASSERT_TRUE(device); + EXPECT_FALSE(DrawPopup(popup, device.get())); + EXPECT_EQ(0xffffffffu, GetCenterPixel(bitmap.Get())); + popup->SetOpenState(true); + EXPECT_TRUE(DrawPopup(popup, device.get())); + EXPECT_EQ(0xffffff00u, GetCenterPixel(bitmap.Get())); + ASSERT_TRUE(popup->GetAnnotDict()->KeyExist("AP")); + const uint32_t object_count = document_->GetLastObjNum(); + popup->SetOpenState(false); + EXPECT_FALSE(DrawPopup(popup, device.get())); + EXPECT_EQ(0xffffffffu, GetCenterPixel(bitmap.Get())); + popup->SetOpenState(true); + EXPECT_TRUE(DrawPopup(popup, device.get())); + EXPECT_EQ(0xffffff00u, GetCenterPixel(bitmap.Get())); + EXPECT_EQ(object_count, document_->GetLastObjNum()); +} + +INSTANTIATE_TEST_SUITE_P(All, CPDFPopupRenderTest, testing::Bool()); diff --git a/core/fpdfdoc/cpdf_filespec.cpp b/core/fpdfdoc/cpdf_filespec.cpp index 814e9f1cec..49fad3a6ec 100644 --- a/core/fpdfdoc/cpdf_filespec.cpp +++ b/core/fpdfdoc/cpdf_filespec.cpp @@ -82,16 +82,11 @@ WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) { return ChangeSlashToPlatform(view.Substr(1)); } if (view[2] == L'/') { - WideString result; - result += view[1]; - result += L':'; - result += ChangeSlashToPlatform(view.Substr(2)); - return result; + WideString platform_path = ChangeSlashToPlatform(view.Substr(2)); + return WideString({view.Substr(1, 1), L':', platform_path.AsStringView()}); } - WideString result; - result += L'\\'; - result += ChangeSlashToPlatform(view); - return result; + WideString platform_path = ChangeSlashToPlatform(view); + return WideString({L'\\', platform_path.AsStringView()}); #else return filepath; #endif diff --git a/core/fpdfdoc/cpdf_formfield.cpp b/core/fpdfdoc/cpdf_formfield.cpp index 2e7c100327..f98d6f31ff 100644 --- a/core/fpdfdoc/cpdf_formfield.cpp +++ b/core/fpdfdoc/cpdf_formfield.cpp @@ -118,7 +118,8 @@ WideString CPDF_FormField::GetFullNameForDict( if (full_name.IsEmpty()) { full_name = std::move(short_name); } else { - full_name = short_name + L'.' + full_name; + full_name = WideString( + {short_name.AsStringView(), L'.', full_name.AsStringView()}); } } pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent).Get(); diff --git a/core/fpdfdoc/cpdf_generateap.cpp b/core/fpdfdoc/cpdf_generateap.cpp index 8c920b63d0..67ee96c595 100644 --- a/core/fpdfdoc/cpdf_generateap.cpp +++ b/core/fpdfdoc/cpdf_generateap.cpp @@ -601,9 +601,10 @@ ByteString GetPopupContentsString(CPDF_Document* doc, const CPDF_Dictionary& annot_dict, RetainPtr default_font, const ByteString& font_name) { - WideString value(annot_dict.GetUnicodeTextFor(pdfium::form_fields::kT)); - value += L'\n'; - value += annot_dict.GetUnicodeTextFor(pdfium::annotation::kContents); + WideString title = annot_dict.GetUnicodeTextFor(pdfium::form_fields::kT); + WideString contents = + annot_dict.GetUnicodeTextFor(pdfium::annotation::kContents); + WideString value({title.AsStringView(), L'\n', contents.AsStringView()}); CPVT_FontMap map(doc, nullptr, std::move(default_font), font_name); CPVT_VariableText::Provider prd(&map); diff --git a/core/fpdfdoc/cpdf_interactiveform.cpp b/core/fpdfdoc/cpdf_interactiveform.cpp index c614ce631c..70f9be1db4 100644 --- a/core/fpdfdoc/cpdf_interactiveform.cpp +++ b/core/fpdfdoc/cpdf_interactiveform.cpp @@ -328,11 +328,10 @@ RetainPtr InitDict(CPDF_Document* document) { } } } - ByteString default_appearance; - if (font) { - default_appearance = "/" + PDF_NameEncode(base_name) + " 12 Tf "; - } - default_appearance += "0 g"; + ByteString default_appearance = + font ? ByteString( + {'/', PDF_NameEncode(base_name).AsStringView(), " 12 Tf 0 g"}) + : ByteString("0 g"); form_dict->SetNewFor("DA", std::move(default_appearance)); return form_dict; } diff --git a/core/fpdftext/BUILD.gn b/core/fpdftext/BUILD.gn index cea247e6b5..8a27e32d52 100644 --- a/core/fpdftext/BUILD.gn +++ b/core/fpdftext/BUILD.gn @@ -17,11 +17,13 @@ source_set("fpdftext") { "unicodenormalizationdata.h", ] configs += [ "../../:pdfium_strict_config" ] + public_deps = [ + "../fpdfapi/page", + "../fxcrt", + ] deps = [ "../fpdfapi/font", - "../fpdfapi/page", "../fpdfapi/parser", - "../fxcrt", ] visibility = [ "../../*" ] } diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp index 70d098df34..cba1d1b6ac 100644 --- a/core/fpdftext/cpdf_textpage.cpp +++ b/core/fpdftext/cpdf_textpage.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -806,14 +807,22 @@ CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation() continue; } + const CFX_FloatRect& rect = page_obj->GetRect(); + if (!std::isfinite(rect.left) || + !std::isfinite(rect.right) || + !std::isfinite(rect.bottom) || + !std::isfinite(rect.top)) { + continue; + } + int32_t minH = static_cast( - std::clamp(page_obj->GetRect().left, 0.0f, nPageWidth)); + std::clamp(rect.left, 0.0f, nPageWidth)); int32_t maxH = static_cast( - std::clamp(page_obj->GetRect().right, 0.0f, nPageWidth)); + std::clamp(rect.right, 0.0f, nPageWidth)); int32_t minV = static_cast( - std::clamp(page_obj->GetRect().bottom, 0.0f, nPageHeight)); + std::clamp(rect.bottom, 0.0f, nPageHeight)); int32_t maxV = static_cast( - std::clamp(page_obj->GetRect().top, 0.0f, nPageHeight)); + std::clamp(rect.top, 0.0f, nPageHeight)); if (minH >= maxH || minV >= maxV) { continue; } @@ -831,7 +840,7 @@ CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation() nEndV = std::max(nEndV, maxV); if (fLineHeight <= 0.0f) { - fLineHeight = page_obj->GetRect().Height(); + fLineHeight = rect.Height(); } } const int32_t nDoubleLineHeight = 2 * fLineHeight; diff --git a/core/fxcodec/BUILD.gn b/core/fxcodec/BUILD.gn index e358904c74..7c8c1832e7 100644 --- a/core/fxcodec/BUILD.gn +++ b/core/fxcodec/BUILD.gn @@ -132,7 +132,10 @@ source_set("fxcodec") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - public_deps = [ "../fxcrt" ] + public_deps = [ + "../../third_party:fx_lcms2", + "../fxcrt", + ] deps = [ "../../third_party:lcms2", "../../third_party:libopenjpeg2", @@ -146,7 +149,10 @@ source_set("fxcodec") { "brotli/brotli_decoder.cpp", "brotli/brotli_decoder.h", ] - deps += [ "//third_party/brotli:dec" ] + deps += [ + "//third_party/brotli:dec", + "//third_party/brotli:headers", + ] } if (pdf_enable_xfa) { sources += [ @@ -166,6 +172,7 @@ source_set("fxcodec") { "tiff/libtiff_tiff_context.cpp", "tiff/libtiff_tiff_context.h", ] + public_deps += [ "../fxge" ] deps += [ "../../third_party:tiff" ] if (pdf_enable_rust_bmp) { sources += [ @@ -222,6 +229,7 @@ source_set("fxcodec") { "codec_memory_sk_stream.cpp", "codec_memory_sk_stream.h", ] + public_deps += [ "//skia" ] } } @@ -248,8 +256,13 @@ source_set("fxcodec") { "jpeg/jpeg_common.c", "jpeg/jpeg_common.h", ] + public_deps += [ "//third_party/libjpeg_turbo:libjpeg_headers" ] deps += [ "//third_party:jpeg" ] } + + if (pdf_use_libyuv) { + deps += [ "//third_party/libyuv" ] + } visibility = [ "../../*" ] } @@ -258,6 +271,7 @@ pdfium_unittest_source_set("unittests") { "basic/a85_unittest.cpp", "basic/rle_unittest.cpp", "flate/flatemodule_unittest.cpp", + "image_predictors_unittest.cpp", "jbig2/jbig2_bit_stream_unittest.cpp", "jbig2/jbig2_image_unittest.cpp", "jpeg/jpegmodule_unittest.cpp", @@ -265,6 +279,8 @@ pdfium_unittest_source_set("unittests") { ] deps = [ ":fxcodec", + "../../testing:path_service", + "../../testing:test_support", "../../third_party:libopenjpeg2", "../fpdfapi/parser", "../fxge", diff --git a/core/fxcodec/DEPS b/core/fxcodec/DEPS index b19b06b0db..1e9528069f 100644 --- a/core/fxcodec/DEPS +++ b/core/fxcodec/DEPS @@ -1,8 +1,11 @@ specific_include_rules = { - "codec_memory_sk_stream\.(cpp|h)": [ + "codec_memory_sk_stream\\.(cpp|h)": [ '+third_party/skia/include', ], - "codec_memory_sk_stream_unittest\.cpp": [ + "codec_memory_sk_stream_unittest\\.cpp": [ '+third_party/skia/include', ], + "fx_codec\\.cpp": [ + '+third_party/libyuv/include/libyuv/planar_functions.h', + ], } diff --git a/core/fxcodec/bmp/DEPS b/core/fxcodec/bmp/DEPS index b4d51b75ae..680b9ae6bc 100644 --- a/core/fxcodec/bmp/DEPS +++ b/core/fxcodec/bmp/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "skia_bmp_context\.cpp": [ + "skia_bmp_context\\.cpp": [ '+third_party/skia/include', ], } diff --git a/core/fxcodec/brotli/brotli_embeddertest.cpp b/core/fxcodec/brotli/brotli_embeddertest.cpp index 5fd9a62a66..473eaf09e1 100644 --- a/core/fxcodec/brotli/brotli_embeddertest.cpp +++ b/core/fxcodec/brotli/brotli_embeddertest.cpp @@ -93,7 +93,7 @@ TEST_F(BrotliEnabledEmbedderTest, BrotliDecodeParms) { ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get()); ASSERT_TRUE(bitmap); - CompareBitmapWithExpectationSuffix(bitmap.get(), "brotli_decode_parms"); + CompareBitmap(bitmap.get(), "brotli_decode_parms"); } TEST_F(BrotliEnabledEmbedderTest, BrotliWithLength1Argument) { diff --git a/core/fxcodec/fax/faxmodule.cpp b/core/fxcodec/fax/faxmodule.cpp index 047ad32a33..20418c37e1 100644 --- a/core/fxcodec/fax/faxmodule.cpp +++ b/core/fxcodec/fax/faxmodule.cpp @@ -26,6 +26,7 @@ #include "core/fxcrt/fx_2d_size.h" #include "core/fxcrt/fx_ceil_div.h" #include "core/fxcrt/fx_memory.h" +#include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/numerics/safe_conversions.h" #include "core/fxcrt/raw_span.h" #include "core/fxcrt/span.h" @@ -363,7 +364,13 @@ void FaxG4GetRow(pdfium::span src_buf, ++run_len1; } - a1 = a0 + run_len1; + FX_SAFE_INT32 safe_a1 = a0; + safe_a1 += run_len1; + if (!safe_a1.IsValid()) { + return; + } + + a1 = safe_a1.ValueOrDie(); if (!a0color) { FaxFillBits(dest_buf, columns, a0, a1); } @@ -392,7 +399,14 @@ void FaxG4GetRow(pdfium::span src_buf, break; } } - a2 = a1 + run_len2; + + FX_SAFE_INT32 safe_a2 = a1; + safe_a2 += run_len2; + if (!safe_a2.IsValid()) { + return; + } + + a2 = safe_a2.ValueOrDie(); if (a0color) { FaxFillBits(dest_buf, columns, a1, a2); } @@ -468,7 +482,13 @@ void FaxG4GetRow(pdfium::span src_buf, int b2; FaxG4FindB1B2(ref_buf, columns, a0, a0color, &b1, &b2); - a1 = b1 + v_delta; + FX_SAFE_INT32 safe_a1 = b1; + safe_a1 += v_delta; + if (!safe_a1.IsValid()) { + return; + } + + a1 = safe_a1.ValueOrDie(); if (!a0color) { FaxFillBits(dest_buf, columns, a0, a1); } diff --git a/core/fxcodec/fx_codec.cpp b/core/fxcodec/fx_codec.cpp index 7a526c9a48..3eef9272a7 100644 --- a/core/fxcodec/fx_codec.cpp +++ b/core/fxcodec/fx_codec.cpp @@ -6,13 +6,18 @@ #include "core/fxcodec/fx_codec.h" -#include - #include "core/fxcrt/numerics/safe_conversions.h" #include "core/fxcrt/span_util.h" -#include "core/fxcrt/zip.h" #include "core/fxge/dib/fx_dib.h" +#if defined(PDF_USE_LIBYUV) +#include "third_party/libyuv/include/libyuv/planar_functions.h" +#else +#include + +#include "core/fxcrt/zip.h" +#endif + #if BUILDFLAG(IS_WIN) #include "core/fxcodec/basic/basicmodule.h" #include "core/fxcodec/fax/faxmodule.h" @@ -50,6 +55,11 @@ void ReverseRGB(pdfium::span pDestBuf, fxcrt::reinterpret_span>(pSrcBuf).first( 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); +#else if (dst_span.data() == src_span.data()) { for (auto& pix : dst_span) { std::swap(pix.red, pix.blue); @@ -66,6 +76,7 @@ void ReverseRGB(pdfium::span pDestBuf, dst_pix.green = green; dst_pix.blue = red; } +#endif } void RegisterEncoders() { diff --git a/core/fxcodec/image_predictors.cpp b/core/fxcodec/image_predictors.cpp index aa86ee3337..8e9c2773ba 100644 --- a/core/fxcodec/image_predictors.cpp +++ b/core/fxcodec/image_predictors.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -25,14 +26,150 @@ namespace fxcodec { namespace { uint8_t PaethPredictor(uint8_t a, uint8_t b, uint8_t c) { - int p = static_cast(a) + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - if (pa <= pb && pa <= pc) { - return a; + const int bc = static_cast(b) - c; + const int ac = static_cast(a) - c; + const int pa = abs(bc); + const int pb = abs(ac); + const int pc = abs(ac + bc); + // Eagerly selecting between `b` and `c` avoids a short-circuit branch across + // `&&` and lets the compiler emit two branchless conditional selects. + const uint8_t bc_best = pb <= pc ? b : c; + return (pa <= pb && pa <= pc) ? a : bc_best; +} + +// Fixed-size pixel view for per-pixel register carry in Sub, Average, and Paeth +// predictors. PNG predictors operate on raw bytes and never care about color +// order. +template +using Pixel = std::array; + +template +void PredictSubRest(pdfium::span dest_span, + pdfium::span src_span) { + const size_t row_size = dest_span.size(); + if (row_size <= Bpp) { + return; + } + auto src_px = fxcrt::reinterpret_span>(src_span); + auto dest_px = fxcrt::reinterpret_span>(dest_span); + Pixel left = dest_px.front(); + for (auto [s, d] : fxcrt::Zip(src_px.subspan(1u), dest_px.subspan(1u))) { + for (size_t c = 0; c < Bpp; ++c) { + left[c] += s[c]; + } + d = left; + } + for (size_t i = row_size - row_size % Bpp; i < row_size; ++i) { + dest_span[i] = src_span[i] + dest_span[i - Bpp]; + } +} + +void PredictSubRow(pdfium::span dest_span, + pdfium::span src_span, + size_t bpp, + size_t lead) { + // The lead bytes have a zero left neighbor. + fxcrt::Copy(src_span.first(lead), dest_span); + // A Sub-filtered row's channels form independent additive chains; the + // generic loop below re-reads dest_span[i - bpp], a value it stored `bpp` + // iterations earlier, which serializes the loop on store-to-load + // forwarding. Walking the row as pixel arrays and carrying the running + // channel values in a local removes that dependency (and, via fxcrt::Zip(), + // all per-element bounds checks). + switch (bpp) { + case 1: + PredictSubRest<1>(dest_span, src_span); + return; + case 2: + PredictSubRest<2>(dest_span, src_span); + return; + case 3: + PredictSubRest<3>(dest_span, src_span); + return; + case 4: + PredictSubRest<4>(dest_span, src_span); + return; + default: { + const size_t row_size = dest_span.size(); + for (size_t i = lead; i < row_size; ++i) { + dest_span[i] = src_span[i] + dest_span[i - bpp]; + } + return; + } + } +} + +template +void PredictAverageFirstRowRest(pdfium::span dest_span, + pdfium::span src_span) { + const size_t row_size = dest_span.size(); + if (row_size <= Bpp) { + return; + } + auto src_px = fxcrt::reinterpret_span>(src_span); + auto dest_px = fxcrt::reinterpret_span>(dest_span); + Pixel left = dest_px.front(); + for (auto [s, d] : fxcrt::Zip(src_px.subspan(1u), dest_px.subspan(1u))) { + for (size_t c = 0; c < Bpp; ++c) { + left[c] = s[c] + left[c] / 2; + } + d = left; + } + for (size_t i = row_size - row_size % Bpp; i < row_size; ++i) { + dest_span[i] = src_span[i] + dest_span[i - Bpp] / 2; + } +} + +template +void PredictAverageRest(pdfium::span dest_span, + pdfium::span src_span, + pdfium::span last_span) { + const size_t row_size = dest_span.size(); + if (row_size <= Bpp) { + return; + } + auto src_px = fxcrt::reinterpret_span>(src_span); + auto last_px = fxcrt::reinterpret_span>(last_span); + auto dest_px = fxcrt::reinterpret_span>(dest_span); + Pixel left = dest_px.front(); + for (auto [s, u, d] : fxcrt::Zip(src_px.subspan(1u), last_px.subspan(1u), + dest_px.subspan(1u))) { + for (size_t c = 0; c < Bpp; ++c) { + left[c] = s[c] + (u[c] + left[c]) / 2; + } + d = left; + } + for (size_t i = row_size - row_size % Bpp; i < row_size; ++i) { + dest_span[i] = src_span[i] + (last_span[i] + dest_span[i - Bpp]) / 2; + } +} + +template +void PredictPaethRest(pdfium::span dest_span, + pdfium::span src_span, + pdfium::span last_span) { + const size_t row_size = dest_span.size(); + if (row_size <= Bpp) { + return; + } + auto src_px = fxcrt::reinterpret_span>(src_span); + auto last_px = fxcrt::reinterpret_span>(last_span); + auto dest_px = fxcrt::reinterpret_span>(dest_span); + Pixel left = dest_px.front(); + Pixel upper_left = last_px.front(); + for (auto [s, u, d] : fxcrt::Zip(src_px.subspan(1u), last_px.subspan(1u), + dest_px.subspan(1u))) { + for (size_t c = 0; c < Bpp; ++c) { + left[c] = s[c] + PaethPredictor(left[c], u[c], upper_left[c]); + } + upper_left = u; + d = left; + } + for (size_t i = row_size - row_size % Bpp; i < row_size; ++i) { + dest_span[i] = + src_span[i] + + PaethPredictor(dest_span[i - Bpp], last_span[i], last_span[i - Bpp]); } - return pb <= pc ? b : c; } std::optional> PngPredictor( @@ -138,22 +275,6 @@ const DataAndBytesConsumed ApplyPredictor(DataVector decoded_buf, NOTREACHED(); } -// Fixed-size pixel views for the Sub filter's per-channel fast paths. The -// channel names are deliberately positional: PNG predictors operate on raw -// bytes and never care about color order. -struct Pixel3 { - uint8_t c0; - uint8_t c1; - uint8_t c2; -}; - -struct Pixel4 { - uint8_t c0; - uint8_t c1; - uint8_t c2; - uint8_t c3; -}; - void PngPredictLine(pdfium::span dest_span, pdfium::span src_span, pdfium::span last_span, @@ -181,51 +302,7 @@ void PngPredictLine(pdfium::span dest_span, switch (tag) { case 1: { // Sub: left neighbor only, so the first row needs no special casing. - // The lead bytes have a zero left neighbor. - fxcrt::Copy(src_span.first(lead), dest_span); - // A Sub-filtered row's channels form independent additive chains; the - // generic loop below re-reads dest_span[i - bpp], a value it stored `bpp` - // iterations earlier, which serializes the loop on store-to-load - // forwarding. For the common 3- and 4-byte pixel sizes, walking the - // row as pixel structs and carrying the running channel values in a - // local removes that dependency (and, via fxcrt::Zip(), all - // per-element bounds checks). - if (bpp == 3 && row_size >= 3) { - auto src_px = fxcrt::reinterpret_span(src_span); - auto dest_px = fxcrt::reinterpret_span(dest_span); - Pixel3 carry = dest_px.front(); - for (auto [s, d] : - fxcrt::Zip(src_px.subspan(1u), dest_px.subspan(1u))) { - carry.c0 += s.c0; - carry.c1 += s.c1; - carry.c2 += s.c2; - d = carry; - } - for (size_t i = row_size - row_size % 3; i < row_size; ++i) { - dest_span[i] = src_span[i] + dest_span[i - 3]; - } - break; - } - if (bpp == 4 && row_size >= 4) { - auto src_px = fxcrt::reinterpret_span(src_span); - auto dest_px = fxcrt::reinterpret_span(dest_span); - Pixel4 carry = dest_px.front(); - for (auto [s, d] : - fxcrt::Zip(src_px.subspan(1u), dest_px.subspan(1u))) { - carry.c0 += s.c0; - carry.c1 += s.c1; - carry.c2 += s.c2; - carry.c3 += s.c3; - d = carry; - } - for (size_t i = row_size - row_size % 4; i < row_size; ++i) { - dest_span[i] = src_span[i] + dest_span[i - 4]; - } - break; - } - for (size_t i = lead; i < row_size; ++i) { - dest_span[i] = src_span[i] + dest_span[i - bpp]; - } + PredictSubRow(dest_span, src_span, bpp, lead); break; } case 2: { @@ -243,8 +320,24 @@ void PngPredictLine(pdfium::span dest_span, // Average: (left + up) / 2, with the zero cases hoisted. if (last_span.empty()) { fxcrt::Copy(src_span.first(lead), dest_span); - for (size_t i = lead; i < row_size; ++i) { - dest_span[i] = src_span[i] + dest_span[i - bpp] / 2; + switch (bpp) { + case 1: + PredictAverageFirstRowRest<1>(dest_span, src_span); + break; + case 2: + PredictAverageFirstRowRest<2>(dest_span, src_span); + break; + case 3: + PredictAverageFirstRowRest<3>(dest_span, src_span); + break; + case 4: + PredictAverageFirstRowRest<4>(dest_span, src_span); + break; + default: + for (size_t i = lead; i < row_size; ++i) { + dest_span[i] = src_span[i] + dest_span[i - bpp] / 2; + } + break; } break; } @@ -252,8 +345,25 @@ void PngPredictLine(pdfium::span dest_span, fxcrt::Zip(src_span.first(lead), last_span, dest_span)) { d = s + u / 2; } - for (size_t i = lead; i < row_size; ++i) { - dest_span[i] = src_span[i] + (last_span[i] + dest_span[i - bpp]) / 2; + switch (bpp) { + case 1: + PredictAverageRest<1>(dest_span, src_span, last_span); + break; + case 2: + PredictAverageRest<2>(dest_span, src_span, last_span); + break; + case 3: + PredictAverageRest<3>(dest_span, src_span, last_span); + break; + case 4: + PredictAverageRest<4>(dest_span, src_span, last_span); + break; + default: + for (size_t i = lead; i < row_size; ++i) { + dest_span[i] = + src_span[i] + (last_span[i] + dest_span[i - bpp]) / 2; + } + break; } break; } @@ -262,20 +372,33 @@ void PngPredictLine(pdfium::span dest_span, // selects the left neighbor, so the first row reduces to Sub; with // left == upper_left == 0 (lead bytes) it always selects up. if (last_span.empty()) { - fxcrt::Copy(src_span.first(lead), dest_span); - for (size_t i = lead; i < row_size; ++i) { - dest_span[i] = src_span[i] + dest_span[i - bpp]; - } + PredictSubRow(dest_span, src_span, bpp, lead); break; } for (auto [s, u, d] : fxcrt::Zip(src_span.first(lead), last_span, dest_span)) { d = s + u; } - for (size_t i = lead; i < row_size; ++i) { - dest_span[i] = - src_span[i] + PaethPredictor(dest_span[i - bpp], last_span[i], - last_span[i - bpp]); + switch (bpp) { + case 1: + PredictPaethRest<1>(dest_span, src_span, last_span); + break; + case 2: + PredictPaethRest<2>(dest_span, src_span, last_span); + break; + case 3: + PredictPaethRest<3>(dest_span, src_span, last_span); + break; + case 4: + PredictPaethRest<4>(dest_span, src_span, last_span); + break; + default: + for (size_t i = lead; i < row_size; ++i) { + dest_span[i] = + src_span[i] + PaethPredictor(dest_span[i - bpp], last_span[i], + last_span[i - bpp]); + } + break; } break; } diff --git a/core/fxcodec/image_predictors_unittest.cpp b/core/fxcodec/image_predictors_unittest.cpp new file mode 100644 index 0000000000..2b46ac79e8 --- /dev/null +++ b/core/fxcodec/image_predictors_unittest.cpp @@ -0,0 +1,107 @@ +// 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/fxcodec/image_predictors.h" + +#include +#include +#include + +#include + +#include "core/fxcrt/span.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace fxcodec { +namespace { + +uint8_t ReferencePaethPredictor(uint8_t a, uint8_t b, uint8_t c) { + int p = static_cast(a) + b - c; + int pa = abs(p - a); + int pb = abs(p - b); + int pc = abs(p - c); + if (pa <= pb && pa <= pc) { + return a; + } + return pb <= pc ? b : c; +} + +void ReferencePngPredictLine(pdfium::span dest_span, + pdfium::span src_span, + pdfium::span last_span, + size_t row_size, + uint32_t bytes_per_pixel) { + const uint8_t tag = src_span.front(); + src_span = src_span.subspan(1u, row_size); + dest_span = dest_span.first(row_size); + if (!last_span.empty()) { + last_span = last_span.first(row_size); + } + const size_t bpp = bytes_per_pixel; + for (size_t i = 0; i < row_size; ++i) { + const uint8_t left = i >= bpp ? dest_span[i - bpp] : 0; + const uint8_t up = last_span.empty() ? 0 : last_span[i]; + const uint8_t upper_left = + (i >= bpp && !last_span.empty()) ? last_span[i - bpp] : 0; + switch (tag) { + case 1: + dest_span[i] = src_span[i] + left; + break; + case 2: + dest_span[i] = src_span[i] + up; + break; + case 3: + dest_span[i] = src_span[i] + (up + left) / 2; + break; + case 4: + dest_span[i] = + src_span[i] + ReferencePaethPredictor(left, up, upper_left); + break; + default: + dest_span[i] = src_span[i]; + break; + } + } +} + +} // namespace + +TEST(ImagePredictorsTest, PngPredictLineAllTagsAndWidths) { + for (uint8_t tag = 0; tag <= 5; ++tag) { + for (uint32_t bpp : {1u, 2u, 3u, 4u, 6u}) { + for (size_t row_size : + {1u, 2u, 3u, 4u, 5u, 7u, 8u, 12u, 16u, 31u, 64u, 129u}) { + std::vector src(row_size + 1); + std::vector last(row_size); + src[0] = tag; + for (size_t i = 0; i < row_size; ++i) { + src[i + 1] = + static_cast((i * 73 + tag * 19 + bpp * 41) & 0xFF); + last[i] = + static_cast((i * 151 + tag * 31 + bpp * 17) & 0xFF); + } + + // First row (empty last_span). + std::vector dst_ref_first(row_size, 0); + std::vector dst_opt_first(row_size, 0); + ReferencePngPredictLine(dst_ref_first, src, {}, row_size, bpp); + PngPredictLine(dst_opt_first, src, {}, row_size, bpp); + EXPECT_EQ(dst_ref_first, dst_opt_first) + << "First row mismatch for tag=" << static_cast(tag) + << " bpp=" << bpp << " row_size=" << row_size; + + // Subsequent row (non-empty last_span). + std::vector dst_ref(row_size, 0); + std::vector dst_opt(row_size, 0); + ReferencePngPredictLine(dst_ref, src, last, row_size, bpp); + PngPredictLine(dst_opt, src, last, row_size, bpp); + EXPECT_EQ(dst_ref, dst_opt) + << "Row mismatch for tag=" << static_cast(tag) + << " bpp=" << bpp << " row_size=" << row_size; + } + } + } +} + +} // namespace fxcodec diff --git a/core/fxcodec/png/DEPS b/core/fxcodec/png/DEPS index cbd0820fd5..0ab0e82596 100644 --- a/core/fxcodec/png/DEPS +++ b/core/fxcodec/png/DEPS @@ -1,8 +1,8 @@ specific_include_rules = { - "libpng_png_context\.cpp": [ + "libpng_png_context\\.cpp": [ '+third_party/libpng/png.h', ], - "skia_png_context\.cpp": [ + "skia_png_context\\.cpp": [ '+third_party/skia/include', ], } diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn index 4cc4b218f2..854777c19c 100644 --- a/core/fxcrt/BUILD.gn +++ b/core/fxcrt/BUILD.gn @@ -165,9 +165,9 @@ source_set("fxcrt") { "//third_party/fast_float", ] public_deps = [ - "../../:freetype_common", "//third_party/abseil-cpp:absl", "//third_party/icu:icuuc", + "//third_party/icu:icuuc_public", ] if (pdf_use_partition_alloc) { sources += [ "fx_memory_pa.cpp" ] @@ -222,10 +222,10 @@ source_set("unit_test_support") { "fake_time_test.h", ] configs += [ "../../:pdfium_strict_config" ] + public_deps = [ "//testing/gtest" ] deps = [ ":fxcrt", ":test_support", - "//testing/gtest", ] } @@ -291,6 +291,7 @@ pdfium_unittest_source_set("unittests") { deps = [ ":test_support", ":unit_test_support", + "../../testing:test_support", ] pdfium_root_dir = "../../" if (pdf_use_partition_alloc) { diff --git a/core/fxcrt/css/BUILD.gn b/core/fxcrt/css/BUILD.gn index 22bec47ac1..5a4228f0e6 100644 --- a/core/fxcrt/css/BUILD.gn +++ b/core/fxcrt/css/BUILD.gn @@ -55,8 +55,9 @@ source_set("css") { "../../../:pdfium_strict_config", "../../../:pdfium_noshorten_config", ] - deps = [ "../" ] + visibility = [ "../../../*" ] + public_deps = [ "../../fxcrt" ] } pdfium_unittest_source_set("unittests") { diff --git a/core/fxcrt/fx_folder_posix.cpp b/core/fxcrt/fx_folder_posix.cpp index 29888e153f..edbb68d747 100644 --- a/core/fxcrt/fx_folder_posix.cpp +++ b/core/fxcrt/fx_folder_posix.cpp @@ -57,7 +57,7 @@ bool FX_PosixFolder::GetNextFile(ByteString* filename, bool* bFolder) { return false; } - ByteString fullpath = path_ + "/" + de->d_name; + ByteString fullpath({path_.AsStringView(), '/', de->d_name}); struct stat deStat; if (stat(fullpath.c_str(), &deStat) < 0) { return false; diff --git a/core/fxcrt/xml/cfx_xmlelement.cpp b/core/fxcrt/xml/cfx_xmlelement.cpp index 6a77e93043..8e9e0694b7 100644 --- a/core/fxcrt/xml/cfx_xmlelement.cpp +++ b/core/fxcrt/xml/cfx_xmlelement.cpp @@ -48,12 +48,10 @@ WideString CFX_XMLElement::GetNamespacePrefix() const { } WideString CFX_XMLElement::GetNamespaceURI() const { - WideString attr(L"xmlns"); WideString wsPrefix = GetNamespacePrefix(); - if (!wsPrefix.IsEmpty()) { - attr += L":"; - attr += wsPrefix; - } + WideString attr = wsPrefix.IsEmpty() + ? WideString(L"xmlns") + : WideString({L"xmlns:", wsPrefix.AsStringView()}); const CFX_XMLNode* pNode = this; while (pNode && pNode->GetType() == Type::kElement) { auto* pElement = static_cast(pNode); @@ -179,10 +177,7 @@ void CFX_XMLElement::RemoveAttribute(const WideString& name) { WideString CFX_XMLElement::AttributeToString(const WideString& name, const WideString& value) { - WideString ret = L" "; - ret += name; - ret += L"=\""; - ret += value.EncodeEntities(); - ret += L"\""; - return ret; + WideString encoded_value = value.EncodeEntities(); + return WideString( + {L' ', name.AsStringView(), L"=\"", encoded_value.AsStringView(), L'"'}); } diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn index 483ed2f560..faf565bf35 100644 --- a/core/fxge/BUILD.gn +++ b/core/fxge/BUILD.gn @@ -103,8 +103,6 @@ source_set("fxge") { "fontdata/chromefontdata/FoxitSerifMM.cpp", "fontdata/chromefontdata/FoxitSymbol.cpp", "fontdata/chromefontdata/chromefontdata.h", - "freetype/fx_freetype.cpp", - "freetype/fx_freetype.h", "fx_font.cpp", "fx_font.h", "fx_fontencoding.h", @@ -121,19 +119,25 @@ source_set("fxge") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - + public_deps = [ "../fxcrt" ] deps = [ # EmbedPDF: CFX_FontRegistry instances variable fonts at registration # (HarfBuzz subset instancer) and records a SHA-256 source hash. "../../third_party/harfbuzz", "../fdrm", - "../fxcrt", + "//third_party/abseil-cpp:absl", ] - - public_deps = [] defines = [] - if (is_component_build || use_system_freetype) { + if (pdf_enable_freetype) { + sources += [ + "freetype/fx_freetype.cpp", + "freetype/fx_freetype.h", + ] + public_deps += [ "../../:freetype_common" ] + } + + if (pdf_enable_freetype && (is_component_build || use_system_freetype)) { # ft_adobe_glyph_list is not exported from the Freetype shared library so we # need it defined in component builds and builds using system freetype. defines += [ "DEFINE_PS_TABLES_DATA" ] @@ -153,6 +157,10 @@ source_set("fxge") { deps += [ "../../third_party:fx_agg" ] } + if (pdf_use_libyuv) { + deps += [ "//third_party/libyuv" ] + } + if (pdf_use_skia) { sources += [ "skia/cfx_dibbase_skia.cpp", @@ -268,9 +276,23 @@ if (pdf_enable_fontations) { } } +source_set("test_support") { + testonly = true + sources = [ + "display_list_device_driver.cpp", + "display_list_device_driver.h", + ] + configs += [ "../../:pdfium_strict_config" ] + deps = [ + ":fxge", + "../fxcrt", + ] +} + pdfium_unittest_source_set("unittests") { sources = [ "cfx_color_unittest.cpp", + "cfx_face_unittest.cpp", "cfx_folderfontinfo_unittest.cpp", "cfx_font_unittest.cpp", "cfx_fontmapper_unittest.cpp", @@ -289,6 +311,9 @@ pdfium_unittest_source_set("unittests") { ] deps = [ ":fxge", + ":test_support", + "../../testing:path_service", + "../../testing:test_support", "../fpdfapi/page", "../fpdfapi/parser", "../fxcrt:test_support", diff --git a/core/fxge/DEPS b/core/fxge/DEPS index 5f0bc43fc7..81e9c22f12 100644 --- a/core/fxge/DEPS +++ b/core/fxge/DEPS @@ -1,5 +1,10 @@ include_rules = [ - '+third_party/rust/cxx', + '+third_party/rust', '+third_party/skia/include', ] +specific_include_rules = { + "cfx_scanlinecompositor\.cpp": [ + '+third_party/libyuv/include/libyuv/convert_argb.h', + ], +} diff --git a/core/fxge/agg/cfx_agg_devicedriver.cpp b/core/fxge/agg/cfx_agg_devicedriver.cpp index 2da2812883..bdfe51ef75 100644 --- a/core/fxge/agg/cfx_agg_devicedriver.cpp +++ b/core/fxge/agg/cfx_agg_devicedriver.cpp @@ -1292,11 +1292,9 @@ void CFX_AggDeviceDriver::SetPathMemoEnabledForTesting(bool enabled) { CFX_AggDeviceDriver::CFX_AggDeviceDriver( RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) + RetainPtr pBackdropBitmap) : bitmap_(std::move(pBitmap)), rgb_byte_order_(bRgbByteOrder), - group_knockout_(bGroupKnockout), backdrop_bitmap_(std::move(pBackdropBitmap)) { CHECK(bitmap_); CHECK_NE(bitmap_->GetFormat(), FXDIB_Format::k1bppMask); @@ -1542,6 +1540,7 @@ bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path, const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) { if (bitmap_->GetBuffer().empty()) { return true; @@ -1553,7 +1552,7 @@ bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path, // itself moved to DrawPathPasses(). if (!g_path_memo_enabled || path.GetPoints().size() > kMemoMaxPoints) { DrawPathPasses(path, pObject2Device, pGraphState, fill_color, stroke_color, - fill_options, nullptr); + group_knockout, fill_options, nullptr); return true; } @@ -1563,7 +1562,7 @@ bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path, if (!path_memo_->SameObjectAsLast(path)) { path_memo_->Restart(path); DrawPathPasses(path, pObject2Device, pGraphState, fill_color, stroke_color, - fill_options, nullptr); + group_knockout, fill_options, nullptr); return true; } const bool fill_pass = @@ -1585,7 +1584,7 @@ bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path, path_memo_->Remember(inputs); path_memo_->BeginRecording(); DrawPathPasses(path, pObject2Device, pGraphState, fill_color, stroke_color, - fill_options, path_memo_.get()); + group_knockout, fill_options, path_memo_.get()); path_memo_->FinishRecording(); return true; } @@ -1598,13 +1597,13 @@ bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path, } if (stroke_pass) { ReplayPass(path_memo_->stroke(), stroke_color, fill_options.full_cover, - group_knockout_); + group_knockout); } return true; } // Its passes were too large to record. DrawPathPasses(path, pObject2Device, pGraphState, fill_color, stroke_color, - fill_options, nullptr); + group_knockout, fill_options, nullptr); return true; } @@ -1616,6 +1615,7 @@ void CFX_AggDeviceDriver::DrawPathPasses( const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options, PathMemo* record) { const float device_width = static_cast(GetPixelWidth()); @@ -1649,7 +1649,7 @@ void CFX_AggDeviceDriver::DrawPathPasses( RasterizeStroke(&rasterizer, &path_data, workspace.solid_stroke(), nullptr, pGraphState, 1, fill_options.stroke_text_mode); RenderRasterizer(rasterizer, stroke_color, fill_options.full_cover, - group_knockout_, record ? &record->stroke() : nullptr); + group_knockout, record ? &record->stroke() : nullptr); ReleaseLargeWorkspace(); return; } @@ -1675,7 +1675,7 @@ void CFX_AggDeviceDriver::DrawPathPasses( RasterizeStroke(&rasterizer, &path_data, workspace.solid_stroke(), &matrix2, pGraphState, matrix1.a, fill_options.stroke_text_mode); RenderRasterizer(rasterizer, stroke_color, fill_options.full_cover, - group_knockout_, record ? &record->stroke() : nullptr); + group_knockout, record ? &record->stroke() : nullptr); ReleaseLargeWorkspace(); return; } @@ -1846,28 +1846,11 @@ bool CFX_AggDeviceDriver::ContinueDIBits( bool CFX_RenderDevice::AttachAggImpl(RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) { - // Unlike the Skia version, all callers pass in a non-null `pBitmap`. + RetainPtr pBackdropBitmap) { CHECK(pBitmap); SetBitmap(pBitmap); SetDeviceDriver(std::make_unique( - std::move(pBitmap), bRgbByteOrder, std::move(pBackdropBitmap), - bGroupKnockout)); + std::move(pBitmap), bRgbByteOrder, std::move(pBackdropBitmap))); return true; } -bool CFX_RenderDevice::CreateAgg(int width, - int height, - FXDIB_Format format, - RetainPtr pBackdropBitmap) { - auto pBitmap = pdfium::MakeRetain(); - if (!pBitmap->Create(width, height, format)) { - return false; - } - - SetBitmap(pBitmap); - SetDeviceDriver(std::make_unique( - std::move(pBitmap), false, std::move(pBackdropBitmap), false)); - return true; -} diff --git a/core/fxge/agg/cfx_agg_devicedriver.h b/core/fxge/agg/cfx_agg_devicedriver.h index 75a3cb77b1..2350b71e5d 100644 --- a/core/fxge/agg/cfx_agg_devicedriver.h +++ b/core/fxge/agg/cfx_agg_devicedriver.h @@ -34,8 +34,7 @@ class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { public: CFX_AggDeviceDriver(RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); + RetainPtr pBackdropBitmap); ~CFX_AggDeviceDriver() override; void InitPlatform(); @@ -66,6 +65,7 @@ class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) override; bool FillRect(const FX_RECT& rect, uint32_t fill_color) override; FX_RECT GetClipBox() const override; @@ -135,6 +135,7 @@ class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options, PathMemo* record); @@ -161,7 +162,6 @@ class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { #endif CFX_FillRenderOptions fill_options_; const bool rgb_byte_order_; - const bool group_knockout_; RetainPtr backdrop_bitmap_; // EmbedPDF(render-speedup): reused path storage and the path memo. std::unique_ptr workspace_; diff --git a/core/fxge/cfx_face.cpp b/core/fxge/cfx_face.cpp index bb686fbfcd..0d98d66d3b 100644 --- a/core/fxge/cfx_face.cpp +++ b/core/fxge/cfx_face.cpp @@ -48,26 +48,28 @@ #if defined(PDF_ENABLE_FONTATIONS) #include "core/fxge/skrifa/src/main.rs.h" #include "third_party/abseil-cpp/absl/cleanup/cleanup.h" -#include "third_party/rust/cxx/v1/cxx.h" +#include "third_party/rust/chromium_crates_io/vendor/cxx-v1/include/cxx.h" #endif namespace { -struct OUTLINE_PARAMS { - UnownedPtr path_; - FT_Pos cur_x_; - FT_Pos cur_y_; -}; - -constexpr float kCoordUnit = 64 * 64.0f; +#if defined(PDF_ENABLE_FREETYPE) constexpr int kThousandthMinInt = std::numeric_limits::min() / 1000; constexpr int kThousandthMaxInt = std::numeric_limits::max() / 1000; - constexpr int kMaxGlyphDimension = 2048; +#endif // defined(PDF_ENABLE_FREETYPE) // Boundary value to avoid integer overflow when adding 1/64th of the value. constexpr int kMaxRectTop = 2114445437; +#if defined(PDF_ENABLE_FREETYPE) +struct OUTLINE_PARAMS { + UnownedPtr path_; + FT_Pos cur_x_; + FT_Pos cur_y_; +}; + +constexpr float kCoordUnit = 64 * 64.0f; int FTPosToCBoxInt(FT_Pos pos) { // Boundary values to avoid integer overflow when multiplied by 1000. @@ -168,6 +170,7 @@ int Outline_CubicTo(const FT_Vector* control1, param->cur_y_ = to->y; return 0; } +#endif // defined(PDF_ENABLE_FREETYPE) #if defined(PDF_ENABLE_FONTATIONS) constexpr float kFixedPpem = 64.0f; @@ -176,16 +179,6 @@ CFX_PointF ToCFXPointF(const skrifa::Point& pt) { return CFX_PointF(pt.x, pt.y); } -void CloseContours(pdfium::span points, - std::vector& contours) { - if (!points.empty()) { - uint16_t last = static_cast(points.size() - 1); - if (contours.empty() || contours.back() != last) { - contours.push_back(last); - } - } -} - std::unique_ptr ConvertOutline(const skrifa::Outline& outline) { if (outline.verbs.empty() || outline.points.empty()) { return nullptr; @@ -248,6 +241,17 @@ std::unique_ptr ConvertOutline(const skrifa::Outline& outline) { return skrifa_path; } +#if defined(PDF_ENABLE_FREETYPE) +void CloseContours(pdfium::span points, + std::vector& contours) { + if (!points.empty()) { + uint16_t last = static_cast(points.size() - 1); + if (contours.empty() || contours.back() != last) { + contours.push_back(last); + } + } +} + struct ConvertedFTOutline { std::vector points; std::vector tags; @@ -262,9 +266,8 @@ FT_Vector ToFTVector(const skrifa::Point& p, const CFX_Matrix& scaled_matrix) { ConvertedFTOutline ConvertToFTOutline(const skrifa::Outline& outline, const CFX_Matrix& matrix, - int upem, + float scale, float x_scale) { - const float scale = kFixedPpem / upem; const CFX_Matrix scaled_matrix(matrix.a * scale * x_scale, matrix.b * scale, matrix.c * scale * x_scale, matrix.d * scale, 0, 0); @@ -329,112 +332,10 @@ ConvertedFTOutline ConvertToFTOutline(const skrifa::Outline& outline, CloseContours(result.points, result.contours); return result; } - -// Backing store for an FT_Outline built from a skrifa outline. FreeType does -// not take ownership of these, so they must outlive the FT_Outline itself. -struct FtOutlineData { - std::vector points; - std::vector tags; - std::vector contours; -}; - -bool AppendFtPoint(FtOutlineData& data, - const skrifa::Point& point, - uint8_t tag) { - float sx = std::round(point.x * kFixedPpem); - float sy = std::round(point.y * kFixedPpem); - if (!std::isfinite(sx) || !std::isfinite(sy) || - !pdfium::IsValueInRangeForNumericType(sx) || - !pdfium::IsValueInRangeForNumericType(sy)) { - return false; - } - data.points.push_back({static_cast(sx), static_cast(sy)}); - data.tags.push_back(tag); - return true; -} - -// Converts `outline`, whose points are in pixels, into the 26.6 fixed point -// form that FreeType's rasterizer expects. Returns nullopt if `outline` is -// empty, is malformed, or does not fit within FreeType's 16-bit counts. -std::optional BuildFtOutline(const skrifa::Outline& outline) { - // FT_Outline::n_points and n_contours are signed 16-bit quantities. - static constexpr size_t kMaxPoints = std::numeric_limits::max(); - - FtOutlineData data; - auto end_contour = [&data] { - if (data.points.empty()) { - return; - } - const uint16_t last = - pdfium::checked_cast(data.points.size() - 1); - if (data.contours.empty() || data.contours.back() != last) { - data.contours.push_back(last); - } - }; - - size_t point_idx = 0; - for (auto verb : outline.verbs) { - switch (verb) { - case skrifa::PathVerb::MoveTo: - if (outline.points.size() - point_idx < 1 || - data.points.size() + 1 > kMaxPoints) { - return std::nullopt; - } - end_contour(); - if (!AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_ON)) { - return std::nullopt; - } - break; - case skrifa::PathVerb::LineTo: - if (outline.points.size() - point_idx < 1 || - data.points.size() + 1 > kMaxPoints) { - return std::nullopt; - } - if (!AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_ON)) { - return std::nullopt; - } - break; - case skrifa::PathVerb::QuadTo: - if (outline.points.size() - point_idx < 2 || - data.points.size() + 2 > kMaxPoints) { - return std::nullopt; - } - if (!AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_CONIC) || - !AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_ON)) { - return std::nullopt; - } - break; - case skrifa::PathVerb::CurveTo: - if (outline.points.size() - point_idx < 3 || - data.points.size() + 3 > kMaxPoints) { - return std::nullopt; - } - if (!AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_CUBIC) || - !AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_CUBIC) || - !AppendFtPoint(data, outline.points[point_idx++], - FT_CURVE_TAG_ON)) { - return std::nullopt; - } - break; - case skrifa::PathVerb::Close: - end_contour(); - break; - } - } - end_contour(); - if (data.points.empty()) { - return std::nullopt; - } - return data; -} +#endif // defined(PDF_ENABLE_FREETYPE) #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) FT_Encoding ToFTEncoding(fxge::FontEncoding encoding) { switch (encoding) { case fxge::FontEncoding::kAdobeCustom: @@ -467,41 +368,59 @@ FT_Encoding ToFTEncoding(fxge::FontEncoding encoding) { return FT_ENCODING_WANSUNG; } } +#endif // defined(PDF_ENABLE_FREETYPE) -fxge::FontEncoding ToFontEncoding(uint32_t ft_encoding) { - switch (ft_encoding) { - case FT_ENCODING_ADOBE_CUSTOM: - return fxge::FontEncoding::kAdobeCustom; - case FT_ENCODING_ADOBE_EXPERT: - return fxge::FontEncoding::kAdobeExpert; - case FT_ENCODING_ADOBE_STANDARD: - return fxge::FontEncoding::kAdobeStandard; - case FT_ENCODING_APPLE_ROMAN: +fxge::FontEncoding CharMapIdPairToFontEncoding( + CFX_Face::CharMapIdPair charmap_id_pair) { + const auto [platform_id, encoding_id] = charmap_id_pair; + if (platform_id == kPlatformAppleUnicode) { + return fxge::FontEncoding::kUnicode; + } + if (platform_id == kPlatformMac) { + if (encoding_id == kMacEncodingRoman) { return fxge::FontEncoding::kAppleRoman; - case FT_ENCODING_BIG5: - return fxge::FontEncoding::kBig5; - case FT_ENCODING_PRC: - return fxge::FontEncoding::kGB2312; - case FT_ENCODING_JOHAB: - return fxge::FontEncoding::kJohab; - case FT_ENCODING_ADOBE_LATIN_1: - return fxge::FontEncoding::kLatin1; - case FT_ENCODING_NONE: - return fxge::FontEncoding::kNone; - case FT_ENCODING_OLD_LATIN_2: - return fxge::FontEncoding::kOldLatin2; - case FT_ENCODING_SJIS: - return fxge::FontEncoding::kSjis; - case FT_ENCODING_MS_SYMBOL: - return fxge::FontEncoding::kSymbol; - case FT_ENCODING_UNICODE: - return fxge::FontEncoding::kUnicode; - case FT_ENCODING_WANSUNG: - return fxge::FontEncoding::kWansung; + } + return fxge::FontEncoding::kNone; } - NOTREACHED(); + if (platform_id == kPlatformIso) { + // FreeType treats all encodings under TT_PLATFORM_ISO as Unicode. + return fxge::FontEncoding::kUnicode; + } + if (platform_id == kPlatformWindows) { + switch (encoding_id) { + case kWindowsEncodingSymbol: + return fxge::FontEncoding::kSymbol; + case kWindowsEncodingUnicode: + case kWindowsEncodingUcs4: + return fxge::FontEncoding::kUnicode; + case kWindowsEncodingSjis: + return fxge::FontEncoding::kSjis; + case kWindowsEncodingGb2312: + return fxge::FontEncoding::kGB2312; + case kWindowsEncodingBig5: + return fxge::FontEncoding::kBig5; + case kWindowsEncodingWansung: + return fxge::FontEncoding::kWansung; + case kWindowsEncodingJohab: + return fxge::FontEncoding::kJohab; + } + } + if (platform_id == kPlatformAdobe) { + switch (encoding_id) { + case kAdobeEncodingStandard: + return fxge::FontEncoding::kAdobeStandard; + case kAdobeEncodingExpert: + return fxge::FontEncoding::kAdobeExpert; + case kAdobeEncodingCustom: + return fxge::FontEncoding::kAdobeCustom; + case kAdobeEncodingLatin1: + return fxge::FontEncoding::kLatin1; + } + } + return fxge::FontEncoding::kNone; } +#if defined(PDF_ENABLE_FREETYPE) FX_RECT FXRectFromFTPos(FT_Pos left, FT_Pos top, FT_Pos right, FT_Pos bottom) { return FX_RECT(pdfium::checked_cast(left), pdfium::checked_cast(top), @@ -554,7 +473,9 @@ class ScopedFaceTransform { private: UnownedPtr const rec_; }; +#endif // defined(PDF_ENABLE_FREETYPE) +#if defined(PDF_ENABLE_FREETYPE) // EmbedPDF(substitute-font-bounds): keeps the design of a variation face (a // multiple master or variable font) and gives it back when it goes out of // scope. A face is shared by every font and document that uses it, so a design @@ -586,6 +507,7 @@ class ScopedFaceVariation { // More than any font has axes; FreeType ignores the excess. std::array design_ = {}; }; +#endif // defined(PDF_ENABLE_FREETYPE) } // namespace @@ -601,10 +523,7 @@ struct SkrifaFontHolder { RetainPtr CFX_Face::New(RetainPtr cache_entry, RetainPtr font_stream, uint32_t face_index) { - CFX_FontMgr* font_mgr = CFX_GEModule::Get()->GetFontMgr(); pdfium::span data = font_stream->span(); - std::unique_ptr skrifa_font; - bool skip_freetype = false; #if defined(PDF_ENABLE_FONTATIONS) if (CFX_GEModule::IsFontations()) { @@ -617,13 +536,16 @@ RetainPtr CFX_Face::New(RetainPtr cache_entry, // keeping a face that only FreeType can read. return nullptr; } - skrifa_font = std::make_unique(std::move(raw_font)); - skip_freetype = skrifa_font->font->font_type() == skrifa::FaceFormat::Type1; + return pdfium::WrapRetain( + new CFX_Face(std::move(cache_entry), std::move(font_stream), nullptr, + new SkrifaFontHolder(std::move(raw_font)))); } #endif // defined(PDF_ENABLE_FONTATIONS) - FT_FaceRec* face_rec = nullptr; - if (!skip_freetype) { +#if defined(PDF_ENABLE_FREETYPE) + if (CFX_GEModule::IsFreetype()) { + CFX_FontMgr* font_mgr = CFX_GEModule::Get()->GetFontMgr(); + FT_FaceRec* face_rec = nullptr; if (FT_New_Memory_Face(font_mgr->GetFTLibrary(), data.data(), pdfium::checked_cast(data.size()), pdfium::checked_cast(face_index), @@ -633,12 +555,13 @@ RetainPtr CFX_Face::New(RetainPtr cache_entry, if (FT_Set_Pixel_Sizes(face_rec, 64, 64) != 0) { return nullptr; } + return pdfium::WrapRetain(new CFX_Face(std::move(cache_entry), + std::move(font_stream), face_rec, + /*skrifa_font=*/nullptr)); } +#endif // defined(PDF_ENABLE_FREETYPE) - // Private ctor. - return pdfium::WrapRetain(new CFX_Face(std::move(cache_entry), - std::move(font_stream), face_rec, - std::move(skrifa_font))); + return nullptr; } // static @@ -651,7 +574,10 @@ wchar_t CFX_Face::UnicodeFromAdobeName(const char* name) { : 0; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return static_cast(FXFT_unicode_from_adobe_name(name) & 0x7FFFFFFF); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } // static @@ -665,9 +591,12 @@ ByteString CFX_Face::AdobeNameFromUnicode(wchar_t unicode) { return ByteString(ByteStringView(result)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) char glyph_name[64]; FXFT_adobe_name_from_unicode(glyph_name, unicode); return ByteString(glyph_name); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } bool CFX_Face::HasGlyphNames() const { @@ -676,7 +605,10 @@ bool CFX_Face::HasGlyphNames() const { return skrifa_font_->font->has_glyph_names(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return !!(GetRec()->face_flags & FT_FACE_FLAG_GLYPH_NAMES); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } bool CFX_Face::IsTtOt() const { @@ -685,8 +617,11 @@ bool CFX_Face::IsTtOt() const { return skrifa_font_->font->is_sfnt(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) const FT_FaceRec* rec = GetRec(); return rec && (rec->face_flags & FT_FACE_FLAG_SFNT); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } ByteString CFX_Face::GetFontFormat() { @@ -704,7 +639,10 @@ ByteString CFX_Face::GetFontFormat() { } } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return ByteString(FT_Get_Font_Format(GetRec())); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } bool CFX_Face::IsTricky() const { @@ -713,16 +651,33 @@ bool CFX_Face::IsTricky() const { return skrifa_font_->font->is_tricky(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return !!(GetRec()->face_flags & FT_FACE_FLAG_TRICKY); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } +#if defined(PDF_ENABLE_FONTATIONS) +bool CFX_Face::IsPostScriptFont() const { + if (!skrifa_font_) { + return false; + } + skrifa::FaceFormat format = skrifa_font_->font->font_type(); + return format == skrifa::FaceFormat::Type1 || + format == skrifa::FaceFormat::Cff; +} +#endif // defined(PDF_ENABLE_FONTATIONS) + bool CFX_Face::IsFixedWidth() const { #if defined(PDF_ENABLE_FONTATIONS) if (CFX_GEModule::IsFontations()) { return skrifa_font_->font->is_fixed_pitch(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return !!(GetRec()->face_flags & FT_FACE_FLAG_FIXED_WIDTH); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #if defined(PDF_ENABLE_XFA) @@ -732,7 +687,10 @@ bool CFX_Face::IsScalable() const { return skrifa_font_->font->is_scalable(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return !!(GetRec()->face_flags & FT_FACE_FLAG_SCALABLE); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #endif // defined(PDF_ENABLE_XFA) @@ -742,8 +700,11 @@ bool CFX_Face::IsItalic() const { return skrifa_font_->font->is_italic(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) const FT_FaceRec* rec = GetRec(); return rec && (rec->style_flags & FT_STYLE_FLAG_ITALIC); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } bool CFX_Face::IsBold() const { @@ -752,8 +713,11 @@ bool CFX_Face::IsBold() const { return skrifa_font_->font->is_bold(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) const FT_FaceRec* rec = GetRec(); return rec && (rec->style_flags & FT_STYLE_FLAG_BOLD); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } ByteString CFX_Face::GetFamilyName() const { @@ -763,7 +727,10 @@ ByteString CFX_Face::GetFamilyName() const { return ByteString(ByteStringView(skrifa_result)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return ByteString(GetRec()->family_name); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } ByteString CFX_Face::GetStyleName() const { @@ -773,7 +740,10 @@ ByteString CFX_Face::GetStyleName() const { return ByteString(skrifa_result.c_str()); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return ByteString(GetRec()->style_name); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } FX_RECT CFX_Face::GetBBox() const { @@ -788,6 +758,7 @@ FX_RECT CFX_Face::GetBBox() const { } } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (CFX_GEModule::IsFreetype()) { const FT_FaceRec* rec = GetRec(); if (rec) { @@ -797,6 +768,7 @@ FX_RECT CFX_Face::GetBBox() const { pdfium::checked_cast(rec->bbox.yMax)); } } +#endif // defined(PDF_ENABLE_FREETYPE) return FX_RECT(-1000, -1000, 1000, 1000); } @@ -806,7 +778,10 @@ uint16_t CFX_Face::GetUnitsPerEm() const { return pdfium::checked_cast(skrifa_font_->font->units_per_em()); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return pdfium::checked_cast(GetRec()->units_per_EM); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } int CFX_Face::EmAdjust(int value) const { @@ -819,7 +794,10 @@ int16_t CFX_Face::GetAscender() const { return static_cast(std::round(skrifa_font_->font->ascent())); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return pdfium::checked_cast(GetRec()->ascender); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } int16_t CFX_Face::GetDescender() const { @@ -828,7 +806,10 @@ int16_t CFX_Face::GetDescender() const { return static_cast(std::round(skrifa_font_->font->descent())); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return pdfium::checked_cast(GetRec()->descender); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } pdfium::span CFX_Face::GetData() const { @@ -842,6 +823,7 @@ size_t CFX_Face::GetSfntTable(uint32_t table, pdfium::span buffer) { rust::Slice(buffer)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return 0; } @@ -857,6 +839,7 @@ size_t CFX_Face::GetSfntTable(uint32_t table, pdfium::span buffer) { return pdfium::checked_cast(length); } } +#endif // defined(PDF_ENABLE_FREETYPE) return 0; } @@ -902,6 +885,7 @@ std::optional> CFX_Face::GetOs2UnicodeRange() { return std::nullopt; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return std::nullopt; } @@ -913,6 +897,8 @@ std::optional> CFX_Face::GetOs2UnicodeRange() { static_cast(os2->ulUnicodeRange2), static_cast(os2->ulUnicodeRange3), static_cast(os2->ulUnicodeRange4)}; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #endif // defined(PDF_ENABLE_XFA) @@ -927,6 +913,7 @@ std::optional> CFX_Face::GetOs2CodePageRange() { return std::nullopt; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return std::nullopt; } @@ -936,6 +923,8 @@ std::optional> CFX_Face::GetOs2CodePageRange() { } return std::array{static_cast(os2->ulCodePageRange1), static_cast(os2->ulCodePageRange2)}; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } std::optional> CFX_Face::GetOs2Panose() { @@ -948,6 +937,7 @@ std::optional> CFX_Face::GetOs2Panose() { return std::nullopt; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return std::nullopt; } @@ -956,6 +946,8 @@ std::optional> CFX_Face::GetOs2Panose() { return std::nullopt; } return std::array{os2->panose[0], os2->panose[1]}; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #endif // defined(PDF_ENABLE_XFA) || BUILDFLAG(IS_ANDROID) || // BUILDFLAG(IS_LINUX) @@ -966,7 +958,10 @@ int CFX_Face::GetGlyphCount() const { return static_cast(skrifa_font_->font->num_glyphs()); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return pdfium::checked_cast(GetRec()->num_glyphs); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } std::unique_ptr CFX_Face::RenderGlyph( @@ -978,33 +973,34 @@ std::unique_ptr CFX_Face::RenderGlyph( FontAntiAliasingMode anti_alias, const CFX_SubstFont* subst_font) { #if defined(PDF_ENABLE_FONTATIONS) - if (CFX_GEModule::IsFontations() && !GetRec()) { + if (CFX_GEModule::IsFontations()) { + if (!skrifa_font_ || !skrifa_font_->font->is_ok()) { + return nullptr; + } skrifa::Outline outline; - if (!skrifa_font_->font->unscaled_outline(glyph_index, outline)) { + bool is_scaled = IsTtOt() && skrifa_font_->font->hinted_outline( + glyph_index, kFixedPpem, + /*is_pedantic=*/false, outline); + if (!is_scaled && + !skrifa_font_->font->unscaled_outline(glyph_index, outline)) { return nullptr; } const int upem = GetUnitsPerEm(); if (upem <= 0) { return nullptr; } - + const float unscaled_advance = + is_scaled ? outline.advance_width * upem / kFixedPpem + : outline.advance_width; CFX_Matrix effective_matrix = matrix; - if (subst_font) { - int skew = subst_font->GetEffectiveSkew(is_cid_font); - if (skew) { - if (is_vertical) { - effective_matrix.b += effective_matrix.d * skew / 100.0f; - } else { - effective_matrix.c -= effective_matrix.a * skew / 100.0f; - } - } - } - + AdjustSubstFontTransform(subst_font, dest_width, unscaled_advance, + is_cid_font, is_vertical, &effective_matrix); +#if defined(PDF_ENABLE_FREETYPE) + const float scale = is_scaled ? 1.0f : kFixedPpem / upem; const bool is_lcd = (anti_alias == FontAntiAliasingMode::kLcd); const float x_scale = is_lcd ? 3.0f : 1.0f; - ConvertedFTOutline converted = - ConvertToFTOutline(outline, effective_matrix, upem, x_scale); + ConvertToFTOutline(outline, effective_matrix, scale, x_scale); if (converted.points.empty() || converted.contours.empty()) { return nullptr; } @@ -1015,7 +1011,7 @@ std::unique_ptr CFX_Face::RenderGlyph( ft_outline.points = converted.points.data(); ft_outline.tags = converted.tags.data(); ft_outline.contours = converted.contours.data(); - ft_outline.flags = FT_OUTLINE_NONE; + ft_outline.flags = FT_OUTLINE_SMART_DROPOUTS; if (subst_font) { int32_t ft_matrix_xx = static_cast(matrix.a * 1024.0f); @@ -1037,10 +1033,40 @@ std::unique_ptr CFX_Face::RenderGlyph( FT_BBox cbox; FT_Outline_Get_CBox(&ft_outline, &cbox); - int x_left = static_cast(cbox.xMin >> 6); - int y_bottom = static_cast(cbox.yMin >> 6); - int x_right = static_cast((cbox.xMax + 63) >> 6); - int y_top = static_cast((cbox.yMax + 63) >> 6); + int x_left; + int y_bottom; + int x_right; + int y_top; + if (anti_alias == FontAntiAliasingMode::kMono) { + // Match FreeType's ft_glyphslot_preset_bitmap() monochrome rounding. + // Coordinates in `cbox` are in 26.6 fixed-point (1/64th pixel). Round + // asymmetrically (+31 / +32) so pixel centers (at 32/64) covered by the + // outline are included. If rounding causes the box to collapse to zero, + // expand by 1 pixel in the direction of the fractional remainder. + x_left = static_cast((cbox.xMin + 31) >> 6); + x_right = static_cast((cbox.xMax + 32) >> 6); + if (x_left == x_right) { + if (((cbox.xMin + 31) & 63) - 31 + ((cbox.xMax + 32) & 63) - 32 < 0) { + --x_left; + } else { + ++x_right; + } + } + y_bottom = static_cast((cbox.yMin + 31) >> 6); + y_top = static_cast((cbox.yMax + 32) >> 6); + if (y_bottom == y_top) { + if (((cbox.yMin + 31) & 63) - 31 + ((cbox.yMax + 32) & 63) - 32 < 0) { + --y_bottom; + } else { + ++y_top; + } + } + } else { + x_left = static_cast(cbox.xMin >> 6); + y_bottom = static_cast(cbox.yMin >> 6); + x_right = static_cast((cbox.xMax + 63) >> 6); + y_top = static_cast((cbox.yMax + 63) >> 6); + } int width = x_right - x_left; int height = y_top - y_bottom; if (width <= 0 || height <= 0) { @@ -1132,8 +1158,12 @@ std::unique_ptr CFX_Face::RenderGlyph( return std::make_unique(CFX_Point(bitmap_left, bitmap_top), std::move(new_bitmap)); +#else + return nullptr; +#endif // defined(PDF_ENABLE_FREETYPE) } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) FT_FaceRec* rec = GetRec(); if (!rec) { return nullptr; @@ -1176,77 +1206,30 @@ std::unique_ptr CFX_Face::RenderGlyph( auto* glyph = rec->glyph; glyph->format = FT_GLYPH_FORMAT_OUTLINE; - bool loaded_fontations_outline = false; -#if defined(PDF_ENABLE_FONTATIONS) - // Backing store for `glyph->outline`; must outlive FT_Render_Glyph(). - std::optional ft_outline; - if (CFX_GEModule::IsFontations()) { - absl::Cleanup outline_cleaner = [glyph] { glyph->outline = FT_Outline{}; }; - if (skrifa_font_ && skrifa_font_->font->is_ok()) { - skrifa::Outline outline; - bool has_outline = false; - if (IsTtOt()) { - has_outline = skrifa_font_->font->hinted_outline( - glyph_index, kFixedPpem, /*is_pedantic=*/false, outline); - } - if (!has_outline) { - has_outline = skrifa_font_->font->scaled_outline(glyph_index, - kFixedPpem, outline); - } - if (has_outline) { - ft_outline = BuildFtOutline(outline); - } - if (ft_outline.has_value()) { - std::move(outline_cleaner).Cancel(); - glyph->outline.n_points = - pdfium::checked_cast(ft_outline->points.size()); - glyph->outline.n_contours = - pdfium::checked_cast(ft_outline->contours.size()); - glyph->outline.points = ft_outline->points.data(); - glyph->outline.tags = ft_outline->tags.data(); - glyph->outline.contours = ft_outline->contours.data(); - glyph->outline.flags = FT_OUTLINE_SMART_DROPOUTS; - FT_Outline_Transform(&glyph->outline, &ft_matrix); - } - } - loaded_fontations_outline = true; + ScopedFaceTransform scoped_transform(GetRec(), &ft_matrix); + int load_flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC; + if (!IsTtOt()) { + load_flags |= FT_LOAD_NO_HINTING; } -#endif // defined(PDF_ENABLE_FONTATIONS) - - if (!loaded_fontations_outline) { - ScopedFaceTransform scoped_transform(GetRec(), &ft_matrix); - int load_flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC; - if (!IsTtOt()) { - load_flags |= FT_LOAD_NO_HINTING; + int error = FT_Load_Glyph(rec, glyph_index, load_flags); + if (error) { + if (load_flags & FT_LOAD_NO_HINTING) { + return nullptr; } - int error = FT_Load_Glyph(rec, glyph_index, load_flags); + load_flags |= FT_LOAD_NO_HINTING; + load_flags &= ~FT_LOAD_PEDANTIC; + error = FT_Load_Glyph(rec, glyph_index, load_flags); if (error) { - if (load_flags & FT_LOAD_NO_HINTING) { - return nullptr; - } - load_flags |= FT_LOAD_NO_HINTING; - load_flags &= ~FT_LOAD_PEDANTIC; - error = FT_Load_Glyph(rec, glyph_index, load_flags); - if (error) { - return nullptr; - } + return nullptr; } } -#if defined(PDF_ENABLE_FONTATIONS) - absl::Cleanup restorer = [glyph, loaded_fontations_outline] { - if (loaded_fontations_outline) { - glyph->outline = FT_Outline{}; - } - }; -#endif // defined(PDF_ENABLE_FONTATIONS) - if (embolden_level > 0) { FT_Outline_Embolden(&glyph->outline, embolden_level); } CFX_FontMgr* font_mgr = CFX_GEModule::Get()->GetFontMgr(); FT_Library_SetLcdFilter(font_mgr->GetFTLibrary(), FT_LCD_FILTER_DEFAULT); - int error = + error = FT_Render_Glyph(glyph, FtRenderModeFromFontAntiAliasingMode(anti_alias)); if (error) { return nullptr; @@ -1281,6 +1264,9 @@ std::unique_ptr CFX_Face::RenderGlyph( new_bitmap->PopulateFromSpan(src_span, src_pitch); } return glyph_bitmap; +#else + return nullptr; +#endif // defined(PDF_ENABLE_FREETYPE) } std::unique_ptr CFX_Face::LoadGlyphPath( @@ -1297,16 +1283,9 @@ std::unique_ptr CFX_Face::LoadGlyphPath( if (upem > 0) { float scale = 1.0f / static_cast(upem); CFX_Matrix matrix(scale, 0, 0, scale, 0, 0); - if (subst_font) { - int skew = subst_font->GetSkew(); - if (skew) { - if (is_vertical) { - matrix.b += matrix.d * skew / 100.0f; - } else { - matrix.c -= matrix.a * skew / 100.0f; - } - } - } + AdjustSubstFontTransform(subst_font, dest_width, + outline.advance_width, /*is_cid_font=*/false, + is_vertical, &matrix); auto path = ConvertOutline(outline); if (path) { path->Transform(matrix); @@ -1318,6 +1297,7 @@ std::unique_ptr CFX_Face::LoadGlyphPath( return nullptr; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return nullptr; } @@ -1381,6 +1361,9 @@ std::unique_ptr CFX_Face::LoadGlyphPath( pPath->ClosePath(); return pPath; +#else + return nullptr; +#endif // defined(PDF_ENABLE_FREETYPE) } int CFX_Face::GetGlyphTTWidth(uint32_t glyph_index) const { @@ -1394,9 +1377,12 @@ int CFX_Face::GetGlyphTTWidth(uint32_t glyph_index) const { return 0; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) const auto* fontglyph = GetRec()->glyph; DCHECK_EQ(glyph_index, fontglyph->glyph_index); return NormalizeFontMetric(fontglyph->metrics.horiAdvance, GetUnitsPerEm()); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } int CFX_Face::GetGlyphWidth(uint32_t glyph_index, @@ -1405,6 +1391,9 @@ int CFX_Face::GetGlyphWidth(uint32_t glyph_index, const CFX_SubstFont* subst_font) { #if defined(PDF_ENABLE_FONTATIONS) if (CFX_GEModule::IsFontations()) { + if (subst_font && subst_font->IsBuiltInGenericFont() && dest_width > 0) { + return dest_width; + } skrifa::Outline outline; if (skrifa_font_->font->unscaled_outline(glyph_index, outline)) { return EmAdjust(static_cast(outline.advance_width)); @@ -1412,6 +1401,7 @@ int CFX_Face::GetGlyphWidth(uint32_t glyph_index, return 0; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) // EmbedPDF(substitute-font-bounds): the face is checked before a design is // set, and the design set for this glyph is undone when the call returns. FT_FaceRec* rec = GetRec(); @@ -1438,6 +1428,8 @@ int CFX_Face::GetGlyphWidth(uint32_t glyph_index, } return EmAdjust(static_cast(horizontal_advance)); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } // EmbedPDF(substitute-font-bounds): a glyph's bounds in the design it is @@ -1446,12 +1438,14 @@ std::optional CFX_Face::GetDrawnGlyphBBox( uint32_t glyph_index, int dest_width, const CFX_SubstFont* subst_font) { +#if defined(PDF_ENABLE_FREETYPE) const bool adjust_variation = subst_font && subst_font->IsBuiltInGenericFont(); ScopedFaceVariation scoped_variation(GetRec(), adjust_variation); if (adjust_variation) { AdjustVariationParams(glyph_index, dest_width, subst_font->GetWeight()); } +#endif // defined(PDF_ENABLE_FREETYPE) if (LoadGlyph(glyph_index, /*scale=*/false) != 0) { return std::nullopt; } @@ -1465,6 +1459,7 @@ ByteString CFX_Face::GetGlyphName(uint32_t glyph_index) { return ByteString(skrifa_result.c_str()); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return ByteString(); } @@ -1472,33 +1467,53 @@ ByteString CFX_Face::GetGlyphName(uint32_t glyph_index) { FT_Get_Glyph_Name(GetRec(), glyph_index, name, sizeof(name)); name[255] = 0; return ByteString(name); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } int CFX_Face::GetCharIndex(uint32_t code) { #if defined(PDF_ENABLE_FONTATIONS) if (CFX_GEModule::IsFontations()) { - if (!GetRec()) { - if (selected_encoding_ == fxge::FontEncoding::kUnicode || - selected_encoding_ == fxge::FontEncoding::kNone) { + const bool unicode_or_none = + selected_encoding_ == fxge::FontEncoding::kUnicode || + selected_encoding_ == fxge::FontEncoding::kNone; + if (IsPostScriptFont()) { + if (unicode_or_none) { return static_cast(skrifa_font_->font->unicode_to_gid(code)); } - if (skrifa_font_->font->font_type() == skrifa::FaceFormat::Type1 && - code <= 0xFF) { + if (code <= 0xFF) { return static_cast( skrifa_font_->font->code_to_gid(static_cast(code))); } return 0; } - FT_CharMap charmap = GetRec()->charmap; - if (charmap && charmap->encoding == FT_ENCODING_UNICODE) { - return static_cast(skrifa_font_->font->unicode_to_gid(code)); + if (selected_charmap_index_.has_value() && !unicode_or_none) { + uint32_t gid = skrifa_font_->font->cmap_char_to_gid( + selected_charmap_index_.value(), code); + if (gid != 0) { + return static_cast(gid); + } + } + if (unicode_or_none) { + uint32_t gid = skrifa_font_->font->unicode_to_gid(code); + if (gid != 0) { + return static_cast(gid); + } + } + if (selected_charmap_index_.has_value()) { + return static_cast(skrifa_font_->font->cmap_char_to_gid( + selected_charmap_index_.value(), code)); } + return 0; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return 0; } return FT_Get_Char_Index(GetRec(), code); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } int CFX_Face::GetNameIndex(const char* name) { @@ -1507,10 +1522,13 @@ int CFX_Face::GetNameIndex(const char* name) { return static_cast(skrifa_font_->font->name_index(name)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return 0; } return FT_Get_Name_Index(GetRec(), name); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } int CFX_Face::LoadGlyph(uint32_t glyph_index, bool scale) { @@ -1519,6 +1537,7 @@ int CFX_Face::LoadGlyph(uint32_t glyph_index, bool scale) { return skrifa_font_->font->has_outline(glyph_index) ? 0 : -1; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return -1; } @@ -1527,6 +1546,8 @@ int CFX_Face::LoadGlyph(uint32_t glyph_index, bool scale) { args |= FT_LOAD_NO_SCALE; } return FT_Load_Glyph(GetRec(), glyph_index, args); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } ByteString CFX_Face::GetPostscriptName() { @@ -1536,19 +1557,26 @@ ByteString CFX_Face::GetPostscriptName() { return ByteString(ByteStringView(skrifa_result)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return ByteString(); } return ByteString(FT_Get_Postscript_Name(GetRec())); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } CFX_Size CFX_Face::GetPixelSize() const { +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return {64, 64}; } int pixel_size_x = GetRec()->size->metrics.x_ppem; int pixel_size_y = GetRec()->size->metrics.y_ppem; return {pixel_size_x, pixel_size_y}; +#else + return {64, 64}; +#endif // defined(PDF_ENABLE_FREETYPE) } std::optional CFX_Face::GetFontGlyphBBox(uint32_t glyph_index) { @@ -1565,6 +1593,7 @@ std::optional CFX_Face::GetFontGlyphBBox(uint32_t glyph_index) { NormalizeFontMetric(bbox.y_max, upem)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (IsTricky()) { int error = FT_Set_Char_Size(GetRec(), 0, 1000 * 64, 72, 72); if (error) { @@ -1602,6 +1631,9 @@ std::optional CFX_Face::GetFontGlyphBBox(uint32_t glyph_index) { GetRec()->glyph->metrics.horiBearingY - GetRec()->glyph->metrics.height, GetRec()->glyph->metrics.horiBearingX + GetRec()->glyph->metrics.width, GetRec()->glyph->metrics.horiBearingY, em, em); +#else + return std::nullopt; +#endif // defined(PDF_ENABLE_FREETYPE) } FX_RECT CFX_Face::GetCharBBox(uint32_t code, int glyph_index) { @@ -1625,6 +1657,7 @@ FX_RECT CFX_Face::GetCharBBox(uint32_t code, int glyph_index) { return rect; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) FT_FaceRec* rec = GetRec(); if (!rec) { return rect; @@ -1667,6 +1700,7 @@ FX_RECT CFX_Face::GetCharBBox(uint32_t code, int glyph_index) { } } } +#endif // defined(PDF_ENABLE_FREETYPE) return rect; } @@ -1681,6 +1715,7 @@ FX_RECT CFX_Face::GetGlyphBBox(uint32_t glyph_index) const { NormalizeFontMetric(bbox.y_min, upem)); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) const auto* glyph = GetRec()->glyph; DCHECK_EQ(glyph_index, glyph->glyph_index); @@ -1691,6 +1726,8 @@ FX_RECT CFX_Face::GetGlyphBBox(uint32_t glyph_index) const { NormalizeFontMetric(top, upem), NormalizeFontMetric(left + glyph->metrics.width, upem), NormalizeFontMetric(top - glyph->metrics.height, upem)); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } std::vector CFX_Face::GetCharCodesAndIndices( @@ -1707,6 +1744,7 @@ std::vector CFX_Face::GetCharCodesAndIndices( return results; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) CharCodeAndIndex char_code_and_index; char_code_and_index.char_code = static_cast( FT_Get_First_Char(GetRec(), &char_code_and_index.glyph_index)); @@ -1724,120 +1762,207 @@ std::vector CFX_Face::GetCharCodesAndIndices( results.push_back(char_code_and_index); } return results; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } CFX_Face::CharMap CFX_Face::GetCurrentCharMap() const { +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + if (selected_charmap_index_.has_value()) { + return reinterpret_cast(selected_charmap_index_.value() + 1); + } + return nullptr; + } +#endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return GetRec() ? GetRec()->charmap : nullptr; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } std::optional CFX_Face::GetCurrentCharMapEncoding() const { #if defined(PDF_ENABLE_FONTATIONS) - if (CFX_GEModule::IsFontations() && !GetRec()) { + if (CFX_GEModule::IsFontations()) { if (selected_encoding_ != fxge::FontEncoding::kNone) { return selected_encoding_; } + if (selected_charmap_index_.has_value()) { + return GetCharMapEncodingByIndex(selected_charmap_index_.value()); + } return fxge::FontEncoding::kUnicode; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec() || !GetRec()->charmap) { return std::nullopt; } - return ToFontEncoding(GetRec()->charmap->encoding); + return CharMapIdPairToFontEncoding( + {.platform_id = GetRec()->charmap->platform_id, + .encoding_id = GetRec()->charmap->encoding_id}); +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } -CFX_Face::CharMapId CFX_Face::GetCharMapIdByIndex(size_t index) const { +CFX_Face::CharMapIdPair CFX_Face::GetCharMapIdPairByIndex(size_t index) const { return {.platform_id = GetCharMapPlatformIdByIndex(index), .encoding_id = GetCharMapEncodingIdByIndex(index)}; } -int CFX_Face::GetCharMapPlatformIdByIndex(size_t index) const { +uint16_t CFX_Face::GetCharMapPlatformIdByIndex(size_t index) const { +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + if (IsPostScriptFont()) { + return index == 0 ? kPlatformAppleUnicode : kPlatformAdobe; + } + skrifa::CharMapInfo info; + if (skrifa_font_->font->get_charmap_info(index, info)) { + return info.platform_id; + } + return 0; + } +#endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return 0; } return GetCharMaps()[index]->platform_id; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } -int CFX_Face::GetCharMapEncodingIdByIndex(size_t index) const { +uint16_t CFX_Face::GetCharMapEncodingIdByIndex(size_t index) const { +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + if (IsPostScriptFont()) { + return index == 0 ? kAppleUnicodeEncodingUnicode2_0 + : kAdobeEncodingCustom; + } + skrifa::CharMapInfo info; + if (skrifa_font_->font->get_charmap_info(index, info)) { + return info.encoding_id; + } + return 0; + } +#endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return 0; } return GetCharMaps()[index]->encoding_id; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } fxge::FontEncoding CFX_Face::GetCharMapEncodingByIndex(size_t index) const { -#if defined(PDF_ENABLE_FONTATIONS) - if (CFX_GEModule::IsFontations() && !GetRec()) { - if (index == 0) { - return fxge::FontEncoding::kUnicode; - } - return fxge::FontEncoding::kAdobeCustom; - } -#endif // defined(PDF_ENABLE_FONTATIONS) - return ToFontEncoding(GetCharMaps()[index]->encoding); + return CharMapIdPairToFontEncoding(GetCharMapIdPairByIndex(index)); } size_t CFX_Face::GetCharMapCount() const { #if defined(PDF_ENABLE_FONTATIONS) - if (CFX_GEModule::IsFontations() && !GetRec()) { - if (skrifa_font_->font->font_type() == skrifa::FaceFormat::Type1) { + if (CFX_GEModule::IsFontations()) { + if (IsPostScriptFont()) { return skrifa_font_->font->encoding() != skrifa::PsEncodingKind::None ? 2 : 1; } - return 1; + return skrifa_font_->font->get_charmap_count(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return GetRec() && GetRec()->charmaps ? pdfium::checked_cast(GetRec()->num_charmaps) : 0; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } +#if defined(PDF_ENABLE_FREETYPE) pdfium::span CFX_Face::GetCharMaps() const { +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + return {}; + } +#endif // defined(PDF_ENABLE_FONTATIONS) if (!GetRec()) { return {}; } - size_t count = GetCharMapCount(); + size_t count = GetRec()->charmaps + ? pdfium::checked_cast(GetRec()->num_charmaps) + : 0; if (count == 0) { return {}; } // SAFETY: required from library to provide correct count. return UNSAFE_BUFFERS({GetRec()->charmaps, count}); } +#endif // defined(PDF_ENABLE_FREETYPE) void CFX_Face::SetCharMap(CharMap map) { +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + if (map) { + size_t index = reinterpret_cast(map) - 1; + if (index < GetCharMapCount()) { + SetCharMapByIndex(index); + return; + } + } + selected_charmap_index_ = std::nullopt; + selected_encoding_ = fxge::FontEncoding::kNone; + return; + } +#endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (GetRec()) { FT_Set_Charmap(GetRec(), static_cast(map)); } +#endif // defined(PDF_ENABLE_FREETYPE) } void CFX_Face::SetCharMapByIndex(size_t index) { CHECK_LT(index, GetCharMapCount()); #if defined(PDF_ENABLE_FONTATIONS) - if (CFX_GEModule::IsFontations() && !GetRec()) { - SelectCharMap(GetCharMapEncodingByIndex(index)); + if (CFX_GEModule::IsFontations()) { + selected_charmap_index_ = index; + selected_encoding_ = GetCharMapEncodingByIndex(index); return; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) // SAFETY: required from library as enforced by check above. SetCharMap(UNSAFE_BUFFERS(GetRec()->charmaps[index])); +#endif // defined(PDF_ENABLE_FREETYPE) } bool CFX_Face::SelectCharMap(fxge::FontEncoding encoding) { #if defined(PDF_ENABLE_FONTATIONS) - if (CFX_GEModule::IsFontations() && !GetRec()) { - if (encoding == fxge::FontEncoding::kUnicode) { - selected_encoding_ = encoding; - return true; + if (CFX_GEModule::IsFontations()) { + if (IsPostScriptFont()) { + if (encoding == fxge::FontEncoding::kUnicode) { + selected_encoding_ = encoding; + selected_charmap_index_ = 0; + return true; + } + if (skrifa_font_->font->encoding() != skrifa::PsEncodingKind::None) { + selected_encoding_ = encoding; + selected_charmap_index_ = 1; + return true; + } + return false; } - if (skrifa_font_->font->font_type() == skrifa::FaceFormat::Type1 && - skrifa_font_->font->encoding() != skrifa::PsEncodingKind::None) { - selected_encoding_ = encoding; - return true; + for (size_t i = 0; i < GetCharMapCount(); ++i) { + if (GetCharMapEncodingByIndex(i) == encoding) { + selected_encoding_ = encoding; + selected_charmap_index_ = i; + return true; + } } return false; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return false; } @@ -1847,6 +1972,8 @@ bool CFX_Face::SelectCharMap(fxge::FontEncoding encoding) { } selected_encoding_ = encoding; return true; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #if defined(PDF_ENABLE_XFA) @@ -1858,7 +1985,10 @@ int CFX_Face::GetNumFaces() const { skrifa::get_num_faces(rust::Slice(data))); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) return GetRec() ? pdfium::checked_cast(GetRec()->num_faces) : 1; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #endif // defined(PDF_ENABLE_XFA) @@ -1902,32 +2032,39 @@ bool CFX_Face::CanEmbed() { return true; } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) if (!GetRec()) { return true; } FT_UShort fstype = FT_Get_FSType_Flags(GetRec()); return (fstype & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING | FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0; +#endif // defined(PDF_ENABLE_FREETYPE) + NOTREACHED(); } #endif // BUILDFLAG(IS_WIN) -CFX_Face::CFX_Face( - RetainPtr cache_entry, - RetainPtr font_stream, - FT_FaceRec* rec, - [[maybe_unused]] std::unique_ptr skrifa_font) +CFX_Face::CFX_Face(RetainPtr cache_entry, + RetainPtr font_stream, + [[maybe_unused]] FT_FaceRec* rec, + [[maybe_unused]] SkrifaFontHolder* skrifa_font) : cache_entry_(std::move(cache_entry)), - font_stream_(std::move(font_stream)), + font_stream_(std::move(font_stream)) +#if defined(PDF_ENABLE_FREETYPE) + , rec_(rec) +#endif #if defined(PDF_ENABLE_FONTATIONS) , - skrifa_font_(std::move(skrifa_font)) + skrifa_font_(skrifa_font) #endif // defined(PDF_ENABLE_FONTATIONS) { -#if defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) && defined(PDF_ENABLE_FONTATIONS) DCHECK(rec_ || skrifa_font_); -#else +#elif defined(PDF_ENABLE_FREETYPE) DCHECK(rec_); +#elif defined(PDF_ENABLE_FONTATIONS) + DCHECK(skrifa_font_); #endif } @@ -1943,6 +2080,7 @@ SkTypeface* CFX_Face::GetOrCreateSkTypeface() { CFX_Face::~CFX_Face() = default; +#if defined(PDF_ENABLE_FREETYPE) void CFX_Face::AdjustVariationParams(int glyph_index, int dest_width, int weight) { @@ -1991,6 +2129,37 @@ void CFX_Face::AdjustVariationParams(int glyph_index, } FT_Set_MM_Design_Coordinates(rec, 2, coords); } +#endif // defined(PDF_ENABLE_FREETYPE) + +#if defined(PDF_ENABLE_FONTATIONS) +void CFX_Face::AdjustSubstFontTransform(const CFX_SubstFont* subst_font, + int dest_width, + float advance_width, + bool is_cid_font, + bool is_vertical, + CFX_Matrix* matrix) const { + if (!subst_font) { + return; + } + if (subst_font->IsBuiltInGenericFont() && dest_width > 0 && + advance_width > 0) { + int glyph_width = EmAdjust(static_cast(advance_width)); + if (glyph_width > 0) { + float scale_x = static_cast(dest_width) / glyph_width; + matrix->a *= scale_x; + matrix->b *= scale_x; + } + } + int skew = subst_font->GetEffectiveSkew(is_cid_font); + if (skew) { + if (is_vertical) { + matrix->b += matrix->d * skew / 100.0f; + } else { + matrix->c -= matrix->a * skew / 100.0f; + } + } +} +#endif // defined(PDF_ENABLE_FONTATIONS) #if defined(PDF_ENABLE_XFA) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) uint32_t CFX_Face::GetFontStyle() { diff --git a/core/fxge/cfx_face.h b/core/fxge/cfx_face.h index e916e1b83e..223b2cc480 100644 --- a/core/fxge/cfx_face.h +++ b/core/fxge/cfx_face.h @@ -19,7 +19,6 @@ #include "core/fxcrt/observed_ptr.h" #include "core/fxcrt/retain_ptr.h" #include "core/fxcrt/span.h" -#include "core/fxge/freetype/fx_freetype.h" #include "core/fxge/fx_font.h" #include "core/fxge/fx_fontencoding.h" @@ -27,9 +26,11 @@ #include "third_party/skia/include/core/SkRefCnt.h" // nogncheck #endif -#if defined(PDF_ENABLE_FONTATIONS) -#include "third_party/rust/cxx/v1/cxx.h" -#endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) +#include "core/fxge/freetype/fx_freetype.h" +#else +using FT_FaceRec = struct FT_FaceRec_; +#endif // defined(PDF_ENABLE_FREETYPE) class CFX_CTTGSUBTable; class CFX_GlyphBitmap; @@ -44,11 +45,7 @@ class CFX_CTTNameTable; class SkTypeface; #endif -#if defined(PDF_ENABLE_FONTATIONS) struct SkrifaFontHolder; -#else -struct SkrifaFontHolder {}; -#endif namespace fxge { enum class FontEncoding : uint32_t; @@ -60,18 +57,24 @@ class CFX_Face final : public Retainable, public Observable { // Note that this corresponds to the cmap header in fonts, and not the cmap // data in PDFs. - struct CharMapId { - friend constexpr bool operator==(const CharMapId&, - const CharMapId&) = default; + struct CharMapIdPair { + friend constexpr bool operator==(const CharMapIdPair&, + const CharMapIdPair&) = default; - int platform_id; - int encoding_id; + uint16_t platform_id; + uint16_t encoding_id; }; // Aliases for some commonly used cmaps. - static constexpr CharMapId kMacRomanCmapId{1, 0}; - static constexpr CharMapId kWindowsSymbolCmapId{3, 0}; - static constexpr CharMapId kWindowsUnicodeCmapId{3, 1}; + static constexpr CharMapIdPair kMacRomanCharMapIdPair{ + .platform_id = kPlatformMac, + .encoding_id = kMacEncodingRoman}; + static constexpr CharMapIdPair kWindowsSymbolCharMapIdPair{ + .platform_id = kPlatformWindows, + .encoding_id = kWindowsEncodingSymbol}; + static constexpr CharMapIdPair kWindowsUnicodeCharMapIdPair{ + .platform_id = kPlatformWindows, + .encoding_id = kWindowsEncodingUnicode}; static RetainPtr New(RetainPtr cache_entry, RetainPtr font_stream, @@ -146,8 +149,8 @@ class CFX_Face final : public Retainable, public Observable { CharMap GetCurrentCharMap() const; std::optional GetCurrentCharMapEncoding() const; - CharMapId GetCharMapIdByIndex(size_t index) const; - int GetCharMapPlatformIdByIndex(size_t index) const; + CharMapIdPair GetCharMapIdPairByIndex(size_t index) const; + uint16_t GetCharMapPlatformIdByIndex(size_t index) const; fxge::FontEncoding GetCharMapEncodingByIndex(size_t index) const; size_t GetCharMapCount() const; int LoadGlyph(uint32_t glyph_index, bool scale); @@ -182,20 +185,33 @@ class CFX_Face final : public Retainable, public Observable { CFX_Face(RetainPtr cache_entry, RetainPtr font_stream, FT_FaceRec* rec, - std::unique_ptr skrifa_font); + SkrifaFontHolder* skrifa_font); ~CFX_Face() override; +#if defined(PDF_ENABLE_FREETYPE) FT_FaceRec* GetRec() { return rec_.get(); } const FT_FaceRec* GetRec() const { return rec_.get(); } +#endif - int GetCharMapEncodingIdByIndex(size_t index) const; + uint16_t GetCharMapEncodingIdByIndex(size_t index) const; CFX_Size GetPixelSize() const; bool IsTricky() const; +#if defined(PDF_ENABLE_FREETYPE) void AdjustVariationParams(int glyph_index, int dest_width, int weight); - +#endif +#if defined(PDF_ENABLE_FONTATIONS) + void AdjustSubstFontTransform(const CFX_SubstFont* subst_font, + int dest_width, + float advance_width, + bool is_cid_font, + bool is_vertical, + CFX_Matrix* matrix) const; +#endif +#if defined(PDF_ENABLE_FREETYPE) pdfium::span GetCharMaps() const; +#endif // Returns the size of the data, or 0 on failure. Only write into `buffer` if // it is large enough to hold the data. @@ -205,6 +221,10 @@ class CFX_Face final : public Retainable, public Observable { std::optional> GetOs2Panose(); #endif +#if defined(PDF_ENABLE_FONTATIONS) + bool IsPostScriptFont() const; +#endif + // `cache_entry_` must outlive `font_stream_`. Faces managed by a cache // and sharing the same `font_stream_` keep the cache entry that indexes // that stream alive via this member while there is at least one face @@ -216,12 +236,15 @@ class CFX_Face final : public Retainable, public Observable { // this member while the `rec_` and `skia_typeface_` is still using it. RetainPtr font_stream_; +#if defined(PDF_ENABLE_FREETYPE) ScopedFXFTFaceRec const rec_; +#endif #if defined(PDF_USE_SKIA) sk_sp skia_typeface_; #endif // defined(PDF_USE_SKIA) #if defined(PDF_ENABLE_FONTATIONS) std::unique_ptr const skrifa_font_; + std::optional selected_charmap_index_; #endif // defined(PDF_ENABLE_FONTATIONS) fxge::FontEncoding selected_encoding_ = fxge::FontEncoding::kNone; }; diff --git a/core/fxge/cfx_face_unittest.cpp b/core/fxge/cfx_face_unittest.cpp new file mode 100644 index 0000000000..692da83385 --- /dev/null +++ b/core/fxge/cfx_face_unittest.cpp @@ -0,0 +1,77 @@ +// 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/cfx_face.h" + +#include + +#include "core/fxcrt/cfx_read_only_span_stream.h" +#include "core/fxge/cfx_gemodule.h" +#include "core/fxge/cfx_glyphbitmap.h" +#include "core/fxge/cfx_path.h" +#include "core/fxge/cfx_substfont.h" +#include "core/fxge/dib/cfx_dibitmap.h" +#include "core/fxge/fontdata/chromefontdata/chromefontdata.h" +#include "core/fxge/fx_font.h" +#include "core/fxge/fx_fontencoding.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(PDF_ENABLE_FONTATIONS) +TEST(CFXFaceTest, BuiltInGenericFontScaling) { + if (!CFX_GEModule::IsFontations()) { + GTEST_SKIP() << "Not a fontations-based renderer"; + } + + auto stream = + pdfium::MakeRetain(kFoxitSansMMFontData); + auto face = CFX_Face::New(nullptr, stream, 0); + ASSERT_TRUE(face); + + CFX_SubstFont subst_font; + subst_font.SetIsBuiltInGenericFont(); + + EXPECT_TRUE(face->SelectCharMap(fxge::FontEncoding::kAdobeCustom)); + int gid_x = face->GetCharIndex(120); + ASSERT_EQ(gid_x, 103); + + int unscaled_width = face->GetGlyphWidth(gid_x, /*dest_width=*/0, + /*weight=*/0, &subst_font); + EXPECT_EQ(unscaled_width, 530); + + int target_dest_width = unscaled_width * 2; + int scaled_width = face->GetGlyphWidth(gid_x, target_dest_width, + /*weight=*/0, &subst_font); + EXPECT_EQ(scaled_width, target_dest_width); + + auto unscaled_path = face->LoadGlyphPath(gid_x, /*dest_width=*/0, + /*is_vertical=*/false, &subst_font); + ASSERT_TRUE(unscaled_path); + + auto scaled_path = face->LoadGlyphPath(gid_x, target_dest_width, + /*is_vertical=*/false, &subst_font); + ASSERT_TRUE(scaled_path); + + float unscaled_path_width = unscaled_path->GetBoundingBox().Width(); + float scaled_path_width = scaled_path->GetBoundingBox().Width(); + EXPECT_NEAR(scaled_path_width, unscaled_path_width * 2.0f, 0.5f); + +#if defined(PDF_ENABLE_FREETYPE) + constexpr CFX_Matrix kMatrix(12.0f, 0, 0, 12.0f, 0, 0); + auto unscaled_bitmap = face->RenderGlyph( + gid_x, /*is_cid_font=*/false, /*is_vertical=*/false, kMatrix, + /*dest_width=*/0, FontAntiAliasingMode::kNormal, &subst_font); + ASSERT_TRUE(unscaled_bitmap); + ASSERT_TRUE(unscaled_bitmap->GetBitmap()); + + auto scaled_bitmap = face->RenderGlyph( + gid_x, /*is_cid_font=*/false, /*is_vertical=*/false, kMatrix, + target_dest_width, FontAntiAliasingMode::kNormal, &subst_font); + ASSERT_TRUE(scaled_bitmap); + ASSERT_TRUE(scaled_bitmap->GetBitmap()); + + EXPECT_GT(scaled_bitmap->GetBitmap()->GetWidth(), + unscaled_bitmap->GetBitmap()->GetWidth()); +#endif // defined(PDF_ENABLE_FREETYPE) +} +#endif // defined(PDF_ENABLE_FONTATIONS) diff --git a/core/fxge/cfx_font.cpp b/core/fxge/cfx_font.cpp index 2be39e2946..532d05e878 100644 --- a/core/fxge/cfx_font.cpp +++ b/core/fxge/cfx_font.cpp @@ -200,6 +200,11 @@ bool CFX_Font::LoadFaceFromSpanStream( uint64_t object_tag) { object_tag_ = object_tag; face_ = CFX_Face::New(nullptr, stream, face_index); +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations() && face_) { + font_data_ = face_->GetData(); + } +#endif return !!face_; } diff --git a/core/fxge/cfx_fontmapper.cpp b/core/fxge/cfx_fontmapper.cpp index e31b728cd7..2477d8d0a6 100644 --- a/core/fxge/cfx_fontmapper.cpp +++ b/core/fxge/cfx_fontmapper.cpp @@ -286,8 +286,8 @@ ByteString GetFontNameFromFace(const CFX_Face& face) { ByteString style_name = face.GetStyleName(); if (!style_name.IsEmpty() && style_name != "Regular") { - family_name += ' '; - family_name += style_name; + return ByteString( + {family_name.AsStringView(), ' ', style_name.AsStringView()}); } return family_name; } diff --git a/core/fxge/cfx_fontmgr.cpp b/core/fxge/cfx_fontmgr.cpp index 0bedea0e35..1a18fb9cf5 100644 --- a/core/fxge/cfx_fontmgr.cpp +++ b/core/fxge/cfx_fontmgr.cpp @@ -48,8 +48,12 @@ sk_sp CreateSkiaFontManagerFallback( return SkFontMgr_New_Fontations_Empty(); } #endif // defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FREETYPE) // This is a SkFontMgr which will use FreeType to decode font data. return SkFontMgr_New_Custom_Empty(); +#else + return nullptr; +#endif // defined(PDF_ENABLE_FREETYPE) #endif // defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) } @@ -68,15 +72,22 @@ sk_sp CreateSkiaFontManager(CFX_FontMgr::FontBackend backend) { } // namespace CFX_FontMgr::CFX_FontMgr([[maybe_unused]] FontBackend backend) - : ft_library_(InitializeFreeType()), + : +#if defined(PDF_ENABLE_FREETYPE) + ft_library_(InitializeFreeType()), +#endif #if defined(PDF_USE_SKIA) font_backend_(backend), skia_fontmgr_(CreateSkiaFontManager(font_backend_)), #endif - builtin_mapper_(std::make_unique()), + builtin_mapper_(std::make_unique()) +#if defined(PDF_ENABLE_FREETYPE) + , ft_library_supports_hinting_( FreeTypeSetLcdFilterMode(ft_library_.get()) || - FreeTypeVersionSupportsHinting(ft_library_.get())) { + FreeTypeVersionSupportsHinting(ft_library_.get())) +#endif +{ } CFX_FontMgr::~CFX_FontMgr() = default; diff --git a/core/fxge/cfx_fontmgr.h b/core/fxge/cfx_fontmgr.h index 390fc9fcca..16437c24a4 100644 --- a/core/fxge/cfx_fontmgr.h +++ b/core/fxge/cfx_fontmgr.h @@ -16,7 +16,10 @@ #include "core/fxcrt/observed_ptr.h" #include "core/fxcrt/retain_ptr.h" #include "core/fxcrt/span.h" + +#if defined(PDF_ENABLE_FREETYPE) #include "core/fxge/freetype/fx_freetype.h" +#endif #if defined(PDF_USE_SKIA) #include "third_party/skia/include/core/SkRefCnt.h" // nogncheck @@ -44,17 +47,21 @@ class CFX_FontMgr { // Always present. CFX_FontMapper* GetBuiltinMapper() const { return builtin_mapper_.get(); } +#if defined(PDF_ENABLE_FREETYPE) FXFT_LibraryRec* GetFTLibrary() const { return ft_library_.get(); } bool FTLibrarySupportsHinting() const { return ft_library_supports_hinting_; } +#endif #if defined(PDF_USE_SKIA) sk_sp MakeSkTypeface(pdfium::span font_span); #endif private: +#if defined(PDF_ENABLE_FREETYPE) // Must come before `builtin_mapper_`. ScopedFXFTLibraryRec const ft_library_; +#endif #if defined(PDF_USE_SKIA) const FontBackend font_backend_; sk_sp skia_fontmgr_; @@ -62,7 +69,9 @@ class CFX_FontMgr { #endif std::unique_ptr builtin_mapper_; std::map> glyph_cache_map_; +#if defined(PDF_ENABLE_FREETYPE) const bool ft_library_supports_hinting_; +#endif }; #if defined(PDF_USE_SKIA) && defined(PDF_USE_SKIA_CUSTOM_FONT_MANAGER) diff --git a/core/fxge/cfx_gemodule.h b/core/fxge/cfx_gemodule.h index 85d6f3c141..e17f37daf7 100644 --- a/core/fxge/cfx_gemodule.h +++ b/core/fxge/cfx_gemodule.h @@ -109,19 +109,20 @@ class CFX_GEModule { } #endif -#if defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FONTATIONS) && defined(PDF_ENABLE_FREETYPE) static bool IsFontations() { return Get()->font_backend_ == CFX_FontMgr::FontBackend::kFontations; } -#endif - static bool IsFreetype() { -#if defined(PDF_ENABLE_FONTATIONS) return Get()->font_backend_ == CFX_FontMgr::FontBackend::kFreeType; + } +#elif defined(PDF_ENABLE_FONTATIONS) + static bool IsFontations() { return true; } +#elif defined(PDF_ENABLE_FREETYPE) + static bool IsFreetype() { return true; } #else - return true; +#error "Neither freetype nor fontations available" #endif - } private: CFX_GEModule(std::optional> user_font_paths, diff --git a/core/fxge/cfx_path.cpp b/core/fxge/cfx_path.cpp index 6b1090f5ae..c22bb45142 100644 --- a/core/fxge/cfx_path.cpp +++ b/core/fxge/cfx_path.cpp @@ -71,6 +71,22 @@ bool PathPointsNeedNormalization(const std::vector& points) { return points.size() > 5; } +// Returns whether `point` lies strictly between `prev` and `next` on a +// horizontal or vertical line, so dropping it does not change the path shape. +bool IsBetweenOnAxisAlignedLine(const CFX_PointF& prev, + const CFX_PointF& point, + const CFX_PointF& next) { + if (prev.y == point.y && 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) { + return (prev.y < point.y && point.y < next.y) || + (prev.y > point.y && point.y > next.y); + } + return false; +} + std::vector GetNormalizedPoints( const std::vector& points) { DCHECK(PathPointsNeedNormalization(points)); @@ -89,12 +105,23 @@ std::vector GetNormalizedPoints( break; } - // If the line does not move, skip this point. const auto& point = *it; if (point.type_ == CFX_Path::Point::Type::kLine && !point.close_figure_ && - !normalized.back().close_figure_ && - point.point_ == normalized.back().point_) { - continue; + !normalized.back().close_figure_) { + // If the line does not move, skip this point. + if (point.point_ == normalized.back().point_) { + continue; + } + + // If the line continues straight on along the same edge, skip this + // point. Some PDF generators emit a rectangle with an extra vertex in + // the middle of an edge. + const auto next = std::next(it); + if (next != points.end() && next->type_ == CFX_Path::Point::Type::kLine && + IsBetweenOnAxisAlignedLine(normalized.back().point_, point.point_, + next->point_)) { + continue; + } } normalized.push_back(point); @@ -432,6 +459,23 @@ bool CFX_Path::IsRect() const { return IsRectImpl(points_); } +std::optional CFX_Path::GetRectOppositeCorner() const { + bool do_normalize = PathPointsNeedNormalization(points_); + std::vector normalized; + if (do_normalize) { + normalized = GetNormalizedPoints(points_); + } + const std::vector& path_points = do_normalize ? normalized : points_; + + if (!IsRectImpl(path_points)) { + return std::nullopt; + } + + // GetNormalizedPoints() keeps the first point, so this is also the opposite + // corner of `points_[0]`. + return path_points[2].point_; +} + std::optional CFX_Path::GetRect(const CFX_Matrix* matrix) const { bool do_normalize = PathPointsNeedNormalization(points_); std::vector normalized; diff --git a/core/fxge/cfx_path.h b/core/fxge/cfx_path.h index 1fab06ff3c..84cc83fef1 100644 --- a/core/fxge/cfx_path.h +++ b/core/fxge/cfx_path.h @@ -59,6 +59,11 @@ class CFX_Path { bool IsRect() const; std::optional GetRect(const CFX_Matrix* matrix) const; + // If IsRect(), returns the corner diagonally opposite to the first point. + // This is not always GetPoints()[2], because a rectangle can have points + // that do not change its shape. + std::optional GetRectOppositeCorner() const; + void Append(const CFX_Path& src, const CFX_Matrix* matrix); void AppendFloatRect(const CFX_FloatRect& rect); void AppendRect(float left, float bottom, float right, float top); diff --git a/core/fxge/cfx_path_unittest.cpp b/core/fxge/cfx_path_unittest.cpp index a1ec3c1dd5..fd8b3894bd 100644 --- a/core/fxge/cfx_path_unittest.cpp +++ b/core/fxge/cfx_path_unittest.cpp @@ -249,6 +249,114 @@ TEST(CFXPath, SixPlusPointRect) { EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); } +TEST(CFXPath, GetRectOppositeCorner) { + CFX_Path path; + path.AppendRect(/*left=*/1, /*bottom=*/2, /*right=*/3, /*top=*/5); + EXPECT_EQ(CFX_PointF(3, 5), path.GetRectOppositeCorner()); + + // A duplicate point makes GetPoints()[2] a point on the first edge. + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); + ASSERT_EQ(CFX_PointF(2, 0), path.GetPoints()[2].point_); + EXPECT_EQ(CFX_PointF(2, 1), path.GetRectOppositeCorner()); + + // So does an extra point in the middle of the first edge. + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine); + ASSERT_EQ(CFX_PointF(2, 0), path.GetPoints()[2].point_); + EXPECT_EQ(CFX_PointF(2, 1), path.GetRectOppositeCorner()); + + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kLine); + EXPECT_FALSE(path.GetRectOppositeCorner().has_value()); +} + +TEST(CFXPath, SixPlusPointRectWithCollinearPoints) { + // A rectangle with an extra point in the middle of its bottom edge, closed + // with "h". This is how some PDF generators emit rectangular clip paths. + CFX_Path path; + path.AppendPoint({0, 1}, CFX_Path::Point::Type::kMove); + path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kLine); + ASSERT_EQ(6u, path.GetPoints().size()); + EXPECT_TRUE(path.IsRect()); + std::optional rect = path.GetRect(nullptr); + ASSERT_TRUE(rect.has_value()); + EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value()); + + constexpr CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70); + rect = path.GetRect(&kScaleMatrix); + ASSERT_TRUE(rect.has_value()); + EXPECT_EQ(CFX_FloatRect(60, 70, 62, 72), rect.value()); + + // An extra point on a vertical edge. + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 0.5f}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine); + EXPECT_TRUE(path.IsRect()); + rect = path.GetRect(nullptr); + ASSERT_TRUE(rect.has_value()); + EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value()); + + // Extra points on two edges. + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({1, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine); + ASSERT_EQ(7u, path.GetPoints().size()); + EXPECT_TRUE(path.IsRect()); + rect = path.GetRect(&kScaleMatrix); + ASSERT_TRUE(rect.has_value()); + EXPECT_EQ(CFX_FloatRect(60, 70, 62, 72), rect.value()); + + // A point that goes past the corner and back is not in between its + // neighbors, so the path is not a rectangle. + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({3, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine); + EXPECT_FALSE(path.IsRect()); + EXPECT_FALSE(path.GetRect(nullptr).has_value()); + + // Collinear points on a diagonal line are not dropped. + path.Clear(); + path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); + path.AppendPoint({1, 1}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine); + path.AppendPoint({2, 3}, CFX_Path::Point::Type::kLine); + path.AppendPoint({0, 3}, CFX_Path::Point::Type::kLine); + path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine); + EXPECT_FALSE(path.IsRect()); + EXPECT_FALSE(path.GetRect(nullptr).has_value()); +} + TEST(CFXPath, NotRect) { CFX_Path path; path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp index 1ccc00e546..c3dde6656c 100644 --- a/core/fxge/cfx_renderdevice.cpp +++ b/core/fxge/cfx_renderdevice.cpp @@ -18,6 +18,7 @@ #include "core/fxcrt/check_op.h" #include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/fx_safe_types.h" +#include "core/fxcrt/notreached.h" #include "core/fxcrt/ptr_util.h" #include "core/fxcrt/span.h" #include "core/fxcrt/zip.h" @@ -40,18 +41,12 @@ #if defined(PDF_USE_AGG) #include "core/fxge/agg/cfx_agg_devicedriver.h" #endif + #if defined(PDF_USE_SKIA) #include "core/fxge/skia/fx_skia_device.h" #include "third_party/skia/include/core/SkTypes.h" // nogncheck #endif -#if BUILDFLAG(IS_WIN) -#include "core/fxge/win32/cgdi_display_driver.h" -#include "core/fxge/win32/cgdi_printer_driver.h" -#include "core/fxge/win32/cps_printer_driver.h" -#include "core/fxge/win32/ctext_only_printer_driver.h" -#endif - namespace { void AdjustGlyphSpace(std::vector* pGlyphAndPos) { @@ -516,36 +511,6 @@ FXDIB_Format GetCreateCompatibleBitmapFormat(bool bytemask_output, return CFX_DIBBase::kPlatformRGBFormat; } -#if BUILDFLAG(IS_WIN) -std::unique_ptr CreateDriver( - HDC hdc, - CFX_PSFontTracker* ps_font_tracker, - const EncoderIface* encoder_iface) { - int device_type = ::GetDeviceCaps(hdc, TECHNOLOGY); - int obj_type = ::GetObjectType(hdc); - const bool use_printer = - device_type == DT_RASPRINTER || device_type == DT_PLOTTER || - device_type == DT_CHARSTREAM || obj_type == OBJ_ENHMETADC; - - if (!use_printer) { - return std::make_unique(hdc); - } - - WindowsPrintMode print_mode = CFX_GEModule::Get()->GetPrintMode(); - if (print_mode == WindowsPrintMode::kEmf || - print_mode == WindowsPrintMode::kEmfImageMasks) { - return std::make_unique(hdc); - } - - if (print_mode == WindowsPrintMode::kTextOnly) { - return std::make_unique(hdc); - } - - return std::make_unique(hdc, print_mode, ps_font_tracker, - encoder_iface); -} -#endif // BUILDFLAG(IS_WIN) - } // namespace CFX_RenderDevice::CFX_RenderDevice() = default; @@ -807,26 +772,16 @@ bool CFX_RenderDevice::DrawPath(const CFX_Path& path, #if defined(PDF_USE_SKIA) if (render_cap_fillstroke_path_) { const bool using_skia = CFX_GEModule::IsSkiaRenderer(); - if (using_skia) { - device_driver_->SetGroupKnockout(true); - } - bool draw_fillstroke_path_result = - device_driver_->DrawPath(path, pObject2Device, pGraphState, - fill_color, stroke_color, fill_options); - - if (using_skia) { - // Restore the group knockout status for `device_driver_` after - // finishing painting a fill-and-stroke path. - device_driver_->SetGroupKnockout(false); - } - return draw_fillstroke_path_result; + return device_driver_->DrawPath( + path, pObject2Device, pGraphState, fill_color, stroke_color, + using_skia || group_knockout_, fill_options); } #endif // defined(PDF_USE_SKIA) return DrawFillStrokePath(path, pObject2Device, pGraphState, fill_color, stroke_color, fill_options); } return device_driver_->DrawPath(path, pObject2Device, pGraphState, fill_color, - stroke_color, fill_options); + stroke_color, group_knockout_, fill_options); } // This can be removed once PDFium entirely relies on Skia @@ -871,19 +826,18 @@ bool CFX_RenderDevice::DrawFillStrokePath( backdrop->Copy(bitmap); } std::unique_ptr bitmap_device = - CFX_RenderDevice::CreateForBitmapWithBackdropAndGroupKnockout( - bitmap, std::move(backdrop), /*group_knockout=*/true); + CFX_RenderDevice::CreateForBitmap(bitmap, std::move(backdrop)); if (!bitmap_device) { return false; } - CFX_Matrix matrix; if (pObject2Device) { matrix = *pObject2Device; } matrix.Translate(-rect.left, -rect.top); if (!bitmap_device->GetDeviceDriver()->DrawPath( - path, &matrix, pGraphState, fill_color, stroke_color, fill_options)) { + path, &matrix, pGraphState, fill_color, stroke_color, + /*group_knockout=*/true, fill_options)) { return false; } FX_RECT src_rect(0, 0, rect.Width(), rect.Height()); @@ -938,7 +892,7 @@ bool CFX_RenderDevice::DrawCosmeticLine( path.AppendPoint(ptMoveTo, CFX_Path::Point::Type::kMove); path.AppendPoint(ptLineTo, CFX_Path::Point::Type::kLine); return device_driver_->DrawPath(path, nullptr, &graph_state, 0, color, - fill_options); + group_knockout_, fill_options); } void CFX_RenderDevice::DrawZeroAreaPath( @@ -978,7 +932,7 @@ void CFX_RenderDevice::DrawZeroAreaPath( path_options.aliased_path = aliased_path; device_driver_->DrawPath(new_path, new_matrix, &graph_state, 0, stroke_color, - path_options); + group_knockout_, path_options); } bool CFX_RenderDevice::GetDIBits(RetainPtr bitmap, @@ -1161,6 +1115,10 @@ bool CFX_RenderDevice::DrawNormalText(pdfium::span pCharPos, const CFX_Matrix& mtText2Device, uint32_t fill_color, const CFX_TextRenderOptions& options) { + if (FXSYS_IsFloatZero(font_size)) { + return true; + } + // `anti_alias` and `normalize` don't affect Skia rendering. FontAntiAliasingMode anti_alias = FontAntiAliasingMode::kMono; bool normalize = false; @@ -1170,7 +1128,13 @@ bool CFX_RenderDevice::DrawNormalText(pdfium::span pCharPos, CFX_TextRenderOptions text_options(options); if (is_text_smooth) { if (GetDeviceType() == DeviceType::kDisplay && bpp_ > 1) { - if (!CFX_GEModule::Get()->GetFontMgr()->FTLibrarySupportsHinting()) { +#if defined(PDF_ENABLE_FREETYPE) + const bool lacks_hinting = + !CFX_GEModule::Get()->GetFontMgr()->FTLibrarySupportsHinting(); +#else + const bool lacks_hinting = false; +#endif + if (lacks_hinting) { // Some Freetype implementations (like the one packaged with Fedora) do // not support hinting due to patents 6219025, 6239783, 6307566, // 6225973, 6243070, 6393145, 6421054, 6282327, and 6624828; the latest @@ -1641,58 +1605,20 @@ CFX_RenderDevice::StateRestorer::~StateRestorer() { device_->RestoreState(false); } -bool CFX_RenderDevice::Attach(RetainPtr pBitmap) { - return AttachWithRgbByteOrder(std::move(pBitmap), false); -} - -bool CFX_RenderDevice::AttachWithRgbByteOrder(RetainPtr pBitmap, - bool bRgbByteOrder) { - return AttachImpl(std::move(pBitmap), bRgbByteOrder, nullptr, false); -} - -bool CFX_RenderDevice::AttachWithBackdropAndGroupKnockout( - RetainPtr pBitmap, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) { - return AttachImpl(std::move(pBitmap), false, std::move(pBackdropBitmap), - bGroupKnockout); -} - -bool CFX_RenderDevice::AttachImpl(RetainPtr pBitmap, - bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) { +bool CFX_RenderDevice::Attach(RetainPtr bitmap, + RetainPtr backdrop_bitmap, + bool rgb_byte_order) { #if defined(PDF_USE_SKIA) if (CFX_GEModule::IsSkiaRenderer()) { - return AttachSkiaImpl(std::move(pBitmap), bRgbByteOrder, - std::move(pBackdropBitmap), bGroupKnockout); + return AttachSkiaImpl(std::move(bitmap), rgb_byte_order, + std::move(backdrop_bitmap)); } #endif #if defined(PDF_USE_AGG) - return AttachAggImpl(std::move(pBitmap), bRgbByteOrder, - std::move(pBackdropBitmap), bGroupKnockout); + return AttachAggImpl(std::move(bitmap), rgb_byte_order, + std::move(backdrop_bitmap)); #else - return false; -#endif -} - -bool CFX_RenderDevice::Create(int width, int height, FXDIB_Format format) { - return CreateWithBackdrop(width, height, format, nullptr); -} - -bool CFX_RenderDevice::CreateWithBackdrop(int width, - int height, - FXDIB_Format format, - RetainPtr backdrop) { -#if defined(PDF_USE_SKIA) - if (CFX_GEModule::IsSkiaRenderer()) { - return CreateSkia(width, height, format, backdrop); - } -#endif -#if defined(PDF_USE_AGG) - return CreateAgg(width, height, format, backdrop); -#else - return false; + NOTREACHED(); #endif } @@ -1700,86 +1626,36 @@ void CFX_RenderDevice::Clear(uint32_t color) { GetDeviceDriver()->Clear(color); } -#if BUILDFLAG(IS_WIN) -CFX_RenderDevice::CFX_RenderDevice(HDC hdc, - CFX_PSFontTracker* ps_font_tracker) { - const EncoderIface* encoder_iface = CFX_GEModule::Get()->GetEncoderIface(); - SetDeviceDriver(CreateDriver(hdc, ps_font_tracker, encoder_iface)); -} - -// static -std::unique_ptr CFX_RenderDevice::CreateForWindowsDC( - HDC hdc, - CFX_PSFontTracker* ps_font_tracker) { - // Private ctor. - return pdfium::WrapUnique(new CFX_RenderDevice(hdc, ps_font_tracker)); -} -#endif - // static std::unique_ptr CFX_RenderDevice::CreateForBitmap( - RetainPtr bitmap, - bool rgb_byte_order) { - // Private ctor. - auto device = pdfium::WrapUnique(new CFX_RenderDevice()); - if (!device->AttachWithRgbByteOrder(std::move(bitmap), rgb_byte_order)) { - return nullptr; - } - return device; -} - -// static -std::unique_ptr -CFX_RenderDevice::CreateForBitmapWithBackdropAndGroupKnockout( RetainPtr bitmap, RetainPtr backdrop_bitmap, - bool group_knockout) { + bool rgb_byte_order) { // Private ctor. auto device = pdfium::WrapUnique(new CFX_RenderDevice()); - if (!device->AttachWithBackdropAndGroupKnockout( - std::move(bitmap), std::move(backdrop_bitmap), group_knockout)) { + if (!device->Attach(std::move(bitmap), std::move(backdrop_bitmap), + rgb_byte_order)) { return nullptr; } return device; } +#if defined(PDF_USE_SKIA) // static -std::unique_ptr CFX_RenderDevice::CreateForNewBitmap( - int width, - int height, - FXDIB_Format format) { - // Private ctor. - auto device = pdfium::WrapUnique(new CFX_RenderDevice()); - if (!device->Create(width, height, format)) { - return nullptr; - } - return device; +std::unique_ptr CFX_RenderDevice::CreateForSkiaCanvas( + SkCanvas& canvas) { + return CreateWithDriver(CFX_SkiaDeviceDriver::Create(canvas)); } +#endif // static -std::unique_ptr -CFX_RenderDevice::CreateForNewBitmapWithBackdrop( - int width, - int height, - FXDIB_Format format, - RetainPtr backdrop) { - // Private ctor. - auto device = pdfium::WrapUnique(new CFX_RenderDevice()); - if (!device->CreateWithBackdrop(width, height, format, std::move(backdrop))) { +std::unique_ptr CFX_RenderDevice::CreateWithDriver( + std::unique_ptr driver) { + if (!driver) { return nullptr; } - return device; -} - -#if defined(PDF_USE_SKIA) -// static -std::unique_ptr CFX_RenderDevice::CreateForSkiaCanvas( - SkCanvas& canvas) { // Private ctor. auto device = pdfium::WrapUnique(new CFX_RenderDevice()); - if (!device->AttachCanvas(canvas)) { - return nullptr; - } + device->SetDeviceDriver(std::move(driver)); return device; } -#endif diff --git a/core/fxge/cfx_renderdevice.h b/core/fxge/cfx_renderdevice.h index 4114b98840..2e3dd76ec9 100644 --- a/core/fxge/cfx_renderdevice.h +++ b/core/fxge/cfx_renderdevice.h @@ -16,15 +16,11 @@ #include "core/fxcrt/span.h" #include "core/fxcrt/unowned_ptr.h" #include "core/fxge/cfx_path.h" +#include "core/fxge/dib/cfx_dibitmap.h" #include "core/fxge/dib/fx_dib.h" #include "core/fxge/renderdevicedriver_iface.h" -#if BUILDFLAG(IS_WIN) -#include -#endif - class CFX_DIBBase; -class CFX_DIBitmap; class CFX_Font; class CFX_GraphStateData; class PauseIndicatorIface; @@ -35,10 +31,6 @@ struct TextCharPos; enum class BorderStyle { kSolid, kDash, kBeveled, kInset, kUnderline }; -#if BUILDFLAG(IS_WIN) -class CFX_PSFontTracker; -#endif - #if defined(PDF_USE_SKIA) class SkCanvas; #endif @@ -56,34 +48,17 @@ class CFX_RenderDevice final { static std::unique_ptr CreateForBitmap( RetainPtr bitmap, + RetainPtr backdrop_bitmap = nullptr, bool rgb_byte_order = false); - static std::unique_ptr - CreateForBitmapWithBackdropAndGroupKnockout( - RetainPtr bitmap, - RetainPtr backdrop_bitmap, - bool group_knockout); - - static std::unique_ptr - CreateForNewBitmap(int width, int height, FXDIB_Format format); - - static std::unique_ptr CreateForNewBitmapWithBackdrop( - int width, - int height, - FXDIB_Format format, - RetainPtr backdrop); - -#if BUILDFLAG(IS_WIN) - static std::unique_ptr CreateForWindowsDC( - HDC hdc, - CFX_PSFontTracker* ps_font_tracker); -#endif - #if defined(PDF_USE_SKIA) static std::unique_ptr CreateForSkiaCanvas( SkCanvas& canvas); #endif + static std::unique_ptr CreateWithDriver( + std::unique_ptr driver); + static CFX_Matrix GetFlipMatrix(float width, float height, float left, @@ -113,6 +88,9 @@ class CFX_RenderDevice final { #endif RetainPtr GetBitmap(); RetainPtr GetBitmap() const; + RenderDeviceDriverIface* GetDeviceDriver() const { + return device_driver_.get(); + } [[nodiscard]] bool CreateCompatibleBitmap(const RetainPtr& pDIB, int width, int height) const; @@ -238,6 +216,9 @@ class CFX_RenderDevice final { // See RenderDeviceDriverIface methods of the same name. bool MultiplyAlpha(float alpha); bool MultiplyAlphaMask(RetainPtr mask); + void SetGroupKnockout(bool group_knockout) { + group_knockout_ = group_knockout; + } #if defined(PDF_USE_SKIA) bool DrawShading(const CPDF_ShadingPattern& pattern, @@ -255,33 +236,14 @@ class CFX_RenderDevice final { private: CFX_RenderDevice(); -#if BUILDFLAG(IS_WIN) - CFX_RenderDevice(HDC hdc, CFX_PSFontTracker* ps_font_tracker); -#endif - [[nodiscard]] bool Attach(RetainPtr pBitmap); - [[nodiscard]] bool AttachWithRgbByteOrder(RetainPtr pBitmap, - bool bRgbByteOrder); - [[nodiscard]] bool AttachWithBackdropAndGroupKnockout( - RetainPtr pBitmap, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); -#if defined(PDF_USE_SKIA) - [[nodiscard]] bool AttachCanvas(SkCanvas& canvas); -#endif - - [[nodiscard]] bool Create(int width, int height, FXDIB_Format format); - [[nodiscard]] bool CreateWithBackdrop(int width, - int height, - FXDIB_Format format, - RetainPtr backdrop); + [[nodiscard]] bool Attach(RetainPtr bitmap, + RetainPtr backdrop_bitmap = nullptr, + bool rgb_byte_order = false); void SetBitmap(RetainPtr bitmap); void SetDeviceDriver(std::unique_ptr pDriver); - RenderDeviceDriverIface* GetDeviceDriver() const { - return device_driver_.get(); - } void InitDeviceInfo(); void UpdateClipBox(); @@ -302,37 +264,18 @@ class CFX_RenderDevice final { uint32_t fill_color, uint8_t fill_alpha); - bool AttachImpl(RetainPtr pBitmap, - bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); - #if defined(PDF_USE_AGG) // Implemented in agg/cfx_agg_devicedriver.cpp bool AttachAggImpl(RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); - - // Implemented in agg/cfx_agg_devicedriver.cpp - bool CreateAgg(int width, - int height, - FXDIB_Format format, - RetainPtr pBackdropBitmap); + RetainPtr pBackdropBitmap); #endif #if defined(PDF_USE_SKIA) // Implemented in skia/fx_skia_device.cpp bool AttachSkiaImpl(RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); - - // Implemented in skia/fx_skia_device.cpp - bool CreateSkia(int width, - int height, - FXDIB_Format format, - RetainPtr pBackdropBitmap); + RetainPtr pBackdropBitmap); #endif RetainPtr bitmap_; @@ -350,6 +293,7 @@ class CFX_RenderDevice final { bool render_cap_shading_ = false; bool render_cap_premultiplied_alpha_ = false; #endif + bool group_knockout_ = false; DeviceType device_type_ = DeviceType::kDisplay; FX_RECT clip_box_; std::unique_ptr device_driver_; diff --git a/core/fxge/cfx_renderdevice_unittest.cpp b/core/fxge/cfx_renderdevice_unittest.cpp index 7103b8ff86..c2fb7b2c99 100644 --- a/core/fxge/cfx_renderdevice_unittest.cpp +++ b/core/fxge/cfx_renderdevice_unittest.cpp @@ -4,6 +4,9 @@ #include "build/build_config.h" +#include +#include + #include "core/fxcrt/fx_coordinates.h" #include "core/fxcrt/fx_coordinates_test_support.h" #include "core/fxcrt/retain_ptr.h" @@ -13,21 +16,48 @@ #include "core/fxge/cfx_renderdevice.h" #include "core/fxge/dib/cfx_dibitmap.h" #include "core/fxge/dib/fx_dib.h" +#include "core/fxge/display_list_device_driver.h" +#include "core/fxge/renderdevicedriver_iface.h" #include "testing/gtest/include/gtest/gtest.h" #if BUILDFLAG(IS_WIN) #include -#include #include #include "core/fxge/cfx_gemodule.h" #include "core/fxge/win32/cfx_psfonttracker.h" -#endif +#include "core/fxge/win32/cgdi_device_driver.h" +#endif // BUILDFLAG(IS_WIN) -TEST(CFXRenderDeviceTest, GetClipBoxDefault) { +namespace { + +std::unique_ptr CreateDeviceForBitmap(int width, int height) { + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(width, height, FXDIB_Format::kBgra)) { + return nullptr; + } + return CFX_RenderDevice::CreateForBitmap(std::move(bitmap)); +} + +} // namespace + +TEST(CFXRenderDeviceTest, CreateWithDriver) { + EXPECT_FALSE(CFX_RenderDevice::CreateWithDriver(nullptr)); + + auto driver = std::make_unique(20, 30); + RenderDeviceDriverIface* driver_ptr = driver.get(); std::unique_ptr device = - CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra); + CFX_RenderDevice::CreateWithDriver(std::move(driver)); + ASSERT_TRUE(device); + EXPECT_EQ(device->GetDeviceDriver(), driver_ptr); + EXPECT_EQ(device->GetWidth(), 20); + EXPECT_EQ(device->GetHeight(), 30); + EXPECT_EQ(device->GetClipBox(), FX_RECT(0, 0, 20, 30)); +} + +TEST(CFXRenderDeviceTest, GetClipBoxDefault) { + std::unique_ptr device = CreateDeviceForBitmap(16, 16); ASSERT_TRUE(device); EXPECT_EQ(FX_RECT(0, 0, 16, 16), device->GetClipBox()); @@ -41,8 +71,7 @@ TEST(CFXRenderDeviceTest, GetClipBoxPathFill) { const CFX_FillRenderOptions fill_options( CFX_FillRenderOptions::FillType::kEvenOdd); - std::unique_ptr device = - CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra); + std::unique_ptr device = CreateDeviceForBitmap(16, 16); ASSERT_TRUE(device); CFX_Path path; @@ -59,8 +88,7 @@ TEST(CFXRenderDeviceTest, GetClipBoxPathStroke) { // Default line width is 1. const CFX_GraphStateData graphics_state; - std::unique_ptr device = - CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra); + std::unique_ptr device = CreateDeviceForBitmap(16, 16); ASSERT_TRUE(device); CFX_Path path; @@ -72,8 +100,7 @@ TEST(CFXRenderDeviceTest, GetClipBoxPathStroke) { } TEST(CFXRenderDeviceTest, GetClipBoxRect) { - std::unique_ptr device = - CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra); + std::unique_ptr device = CreateDeviceForBitmap(16, 16); ASSERT_TRUE(device); EXPECT_TRUE(device->SetClip_Rect({2, 4, 14, 12})); @@ -82,8 +109,7 @@ TEST(CFXRenderDeviceTest, GetClipBoxRect) { } TEST(CFXRenderDeviceTest, GetClipBoxEmpty) { - std::unique_ptr device = - CFX_RenderDevice::CreateForNewBitmap(16, 16, FXDIB_Format::kBgra); + std::unique_ptr device = CreateDeviceForBitmap(16, 16); ASSERT_TRUE(device); EXPECT_TRUE(device->SetClip_Rect({2, 8, 14, 8})); @@ -131,8 +157,8 @@ class CFXWindowsRenderDeviceTest : public testing::Test { // Get a device context with Windows GDI. dc_handle_ = CreateCompatibleDC(nullptr); ASSERT_TRUE(dc_handle_); - device_ = - CFX_RenderDevice::CreateForWindowsDC(dc_handle_, &psfont_tracker_); + device_ = CFX_RenderDevice::CreateWithDriver( + CGdiDeviceDriver::CreateDriver(dc_handle_, &psfont_tracker_)); ASSERT_TRUE(device_); device_->SaveState(); } @@ -212,8 +238,8 @@ TEST(CFXRenderDeviceTest, WindowsEmfPostScriptClipBox) { const FX_RECT large_clip_rect(0, 0, horz_res * 2, vert_res * 2); CFX_PSFontTracker font_tracker; - std::unique_ptr device = - CFX_RenderDevice::CreateForWindowsDC(dc_handle.get(), &font_tracker); + std::unique_ptr device = CFX_RenderDevice::CreateWithDriver( + CGdiDeviceDriver::CreateDriver(dc_handle.get(), &font_tracker)); ASSERT_TRUE(device); EXPECT_TRUE(device->SetClip_Rect(large_clip_rect)); @@ -238,8 +264,8 @@ TEST(CFXRenderDeviceTest, WindowsEmfPostScriptClipRgn) { EXPECT_NE(ERROR, ::SelectClipRgn(dc_handle.get(), clip_rgn.get())); CFX_PSFontTracker font_tracker; - std::unique_ptr device = - CFX_RenderDevice::CreateForWindowsDC(dc_handle.get(), &font_tracker); + std::unique_ptr device = CFX_RenderDevice::CreateWithDriver( + CGdiDeviceDriver::CreateDriver(dc_handle.get(), &font_tracker)); ASSERT_TRUE(device); // TODO(crbug.com/553140224): Clip box should be `large_clip_rect`. diff --git a/core/fxge/dib/cfx_scanlinecompositor.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp index 498f80a929..fd7864330f 100644 --- a/core/fxge/dib/cfx_scanlinecompositor.cpp +++ b/core/fxge/dib/cfx_scanlinecompositor.cpp @@ -7,21 +7,25 @@ #include "core/fxge/dib/cfx_scanlinecompositor.h" #include +#include #include "core/fxcrt/check.h" #include "core/fxcrt/check_op.h" #include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/fx_memcpy_wrappers.h" #include "core/fxcrt/notreached.h" +#include "core/fxcrt/numerics/safe_conversions.h" #include "core/fxcrt/span_util.h" #include "core/fxcrt/stl_util.h" #include "core/fxcrt/zip.h" #include "core/fxge/dib/blend.h" #include "core/fxge/dib/fx_dib.h" -#if defined(PDF_USE_SKIA) -#include +#if defined(PDF_USE_LIBYUV) +#include "third_party/libyuv/include/libyuv/convert_argb.h" +#endif +#if defined(PDF_USE_SKIA) #include "third_party/skia/include/core/SkBlendMode.h" // nogncheck #include "third_party/skia/include/core/SkCanvas.h" // nogncheck #include "third_party/skia/include/core/SkImage.h" // nogncheck @@ -440,9 +444,23 @@ void CompositeRow_Bgr2Bgra_NoBlend_NoClip( int src_Bpp) { const size_t width = dest_span.size(); if (src_Bpp == 3) { - CopyRowToOpaqueBgra(dest_span, - fxcrt::reinterpret_span>( - src_span.first(width * 3))); + auto bgr_span = fxcrt::reinterpret_span>( + src_span.first(width * 3)); +#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); + } else { + static_assert(std::is_same_v>); + libyuv::RAWToARGB(src_bytes, pixel_count * 3, dest_bytes, pixel_count * 4, + pixel_count, 1); + } +#else + CopyRowToOpaqueBgra(dest_span, bgr_span); +#endif return; } CHECK_EQ(src_Bpp, 4); diff --git a/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp b/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp index 6b78d29ce0..cb2ba0d8e3 100644 --- a/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp +++ b/core/fxge/dib/cfx_scanlinecompositor_unittest.cpp @@ -856,3 +856,57 @@ TEST(ScanlineCompositorTest, CompositeRgbBitmapLineBgraPremulLuminosity) { RunPreMultiplyTest(compositor, kSrcScan3, kExpectations3); } #endif // defined(PDF_USE_SKIA) + +TEST(ScanlineCompositorTest, CompositeRgbBitmapLineBgrToBgraNormal) { + static constexpr FX_BGR_STRUCT kBgrSrc[] = { + {.blue = 10, .green = 20, .red = 30}, + {.blue = 40, .green = 50, .red = 60}, + {.blue = 70, .green = 80, .red = 90}, + {.blue = 100, .green = 110, .red = 120}, + {.blue = 130, .green = 140, .red = 150}, + }; + + { + CFX_ScanlineCompositor compositor; + ASSERT_TRUE(compositor.Init(/*dest_format=*/FXDIB_Format::kBgra, + /*src_format=*/FXDIB_Format::kBgr, + /*src_palette=*/{}, + /*mask_color=*/0, + /*blend_type=*/BlendMode::kNormal, + /*bRgbByteOrder=*/false)); + std::array, 5> dest_scan = {}; + compositor.CompositeRgbBitmapLine(pdfium::as_writable_byte_span(dest_scan), + pdfium::as_byte_span(kBgrSrc), + dest_scan.size(), {}); + static constexpr FX_BGRA_STRUCT kExpectedBgra[] = { + {.blue = 10, .green = 20, .red = 30, .alpha = 255}, + {.blue = 40, .green = 50, .red = 60, .alpha = 255}, + {.blue = 70, .green = 80, .red = 90, .alpha = 255}, + {.blue = 100, .green = 110, .red = 120, .alpha = 255}, + {.blue = 130, .green = 140, .red = 150, .alpha = 255}, + }; + EXPECT_THAT(dest_scan, ElementsAreArray(kExpectedBgra)); + } + + { + CFX_ScanlineCompositor compositor; + ASSERT_TRUE(compositor.Init(/*dest_format=*/FXDIB_Format::kBgra, + /*src_format=*/FXDIB_Format::kBgr, + /*src_palette=*/{}, + /*mask_color=*/0, + /*blend_type=*/BlendMode::kNormal, + /*bRgbByteOrder=*/true)); + std::array, 5> dest_scan = {}; + compositor.CompositeRgbBitmapLine(pdfium::as_writable_byte_span(dest_scan), + pdfium::as_byte_span(kBgrSrc), + dest_scan.size(), {}); + static constexpr FX_BGRA_STRUCT kExpectedRgba[] = { + {.blue = 30, .green = 20, .red = 10, .alpha = 255}, + {.blue = 60, .green = 50, .red = 40, .alpha = 255}, + {.blue = 90, .green = 80, .red = 70, .alpha = 255}, + {.blue = 120, .green = 110, .red = 100, .alpha = 255}, + {.blue = 150, .green = 140, .red = 130, .alpha = 255}, + }; + EXPECT_THAT(dest_scan, ElementsAreArray(kExpectedRgba)); + } +} diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp index c0f9782023..68245db260 100644 --- a/core/fxge/dib/cstretchengine.cpp +++ b/core/fxge/dib/cstretchengine.cpp @@ -596,12 +596,40 @@ bool CStretchEngine::ContinueStretchHorz(PauseIndicatorIface* pPause) { uint32_t dest_r = 0; uint32_t dest_g = 0; uint32_t dest_b = 0; - for (auto [weight, src] : fxcrt::Zip(weights, window)) { - uint32_t pixel_weight = weight * src.alpha / 255; - dest_b += pixel_weight * src.blue; - dest_g += pixel_weight * src.green; - dest_r += pixel_weight * src.red; - dest_a += pixel_weight; + // At one and two taps, the loop's own setup costs more than the + // multiply-adds it guards, so those counts get straight-line code. + // The terms are summed in tap order, so the result is identical to + // the loop below. + switch (weights.size()) { + case 1: { + const auto& src0 = window[0]; + const uint32_t pw0 = weights[0] * src0.alpha / 255; + dest_b = pw0 * src0.blue; + dest_g = pw0 * src0.green; + dest_r = pw0 * src0.red; + dest_a = pw0; + break; + } + case 2: { + const auto& src0 = window[0]; + const auto& src1 = window[1]; + const uint32_t pw0 = weights[0] * src0.alpha / 255; + const uint32_t pw1 = weights[1] * src1.alpha / 255; + dest_b = pw0 * src0.blue + pw1 * src1.blue; + dest_g = pw0 * src0.green + pw1 * src1.green; + dest_r = pw0 * src0.red + pw1 * src1.red; + dest_a = pw0 + pw1; + break; + } + default: + for (auto [weight, src] : fxcrt::Zip(weights, window)) { + uint32_t pixel_weight = weight * src.alpha / 255; + dest_b += pixel_weight * src.blue; + dest_g += pixel_weight * src.green; + dest_r += pixel_weight * src.red; + dest_a += pixel_weight; + } + break; } FX_BGRA_STRUCT& dest = dest_pixels[dest_index++]; dest.blue = PixelFromFixed(dest_b); diff --git a/core/fxge/display_list_device_driver.cpp b/core/fxge/display_list_device_driver.cpp new file mode 100644 index 0000000000..c24f62ecbf --- /dev/null +++ b/core/fxge/display_list_device_driver.cpp @@ -0,0 +1,339 @@ +// 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/display_list_device_driver.h" + +#include + +#include + +#include "core/fxcrt/bytestring.h" +#include "core/fxcrt/check_op.h" +#include "core/fxcrt/fx_coordinates.h" +#include "core/fxcrt/span.h" +#include "core/fxge/cfx_fillrenderoptions.h" +#include "core/fxge/cfx_font.h" +#include "core/fxge/cfx_graphstatedata.h" +#include "core/fxge/cfx_path.h" +#include "core/fxge/dib/cfx_dibbase.h" +#include "core/fxge/dib/cfx_dibitmap.h" +#include "core/fxge/text_char_pos.h" + +namespace { + +ByteString FormatFloat(float val) { + if (fabs(val) < 0.00005f) { + val = 0.0f; + } + return ByteString::Format("%.4f", val); +} + +ByteString FormatColor(uint32_t color) { + return ByteString::Format("0x%08x", color); +} + +ByteString FormatPoint(const CFX_PointF& pt) { + return ByteString({'(', FormatFloat(pt.x).AsStringView(), ',', + FormatFloat(pt.y).AsStringView(), ')'}); +} + +ByteString FormatGlyph(const TextCharPos& cp) { + ByteString prefix = + cp.unicode_ != 0 + ? ByteString::Format("(idx=%u,u=U+%04X,origin=", cp.glyph_index_, + cp.unicode_) + : ByteString::Format("(idx=%u,origin=", cp.glyph_index_); + return ByteString( + {prefix.AsStringView(), FormatPoint(cp.origin_).AsStringView(), ')'}); +} + +ByteString FormatDIBInfo(const CFX_DIBBase& bitmap) { + return ByteString::Format("size=[%d,%d] bpp=%d", bitmap.GetWidth(), + bitmap.GetHeight(), bitmap.GetBPP()); +} + +ByteString FormatMatrix(const CFX_Matrix& m) { + return ByteString({'[', FormatFloat(m.a).AsStringView(), ',', + FormatFloat(m.b).AsStringView(), ',', + FormatFloat(m.c).AsStringView(), ',', + FormatFloat(m.d).AsStringView(), ',', + FormatFloat(m.e).AsStringView(), ',', + FormatFloat(m.f).AsStringView(), ']'}); +} + +ByteString FormatPath(const CFX_Path& path) { + ByteString res; + for (size_t i = 0; i < path.GetPoints().size(); ++i) { + if (i > 0) { + res += " "; + } + const CFX_Path::Point& pt = path.GetPoints()[i]; + const char* type_str; + if (pt.type_ == CFX_Path::Point::Type::kMove) { + type_str = "move"; + } else if (pt.type_ == CFX_Path::Point::Type::kBezier) { + type_str = "bezier"; + } else { + CHECK_EQ(pt.type_, CFX_Path::Point::Type::kLine); + type_str = "line"; + } + res += ByteString({type_str, '(', FormatFloat(pt.point_.x).AsStringView(), + ',', FormatFloat(pt.point_.y).AsStringView(), + pt.close_figure_ ? ",close" : "", ')'}); + } + return res; +} + +const char* FormatFillType(CFX_FillRenderOptions::FillType fill_type) { + if (fill_type == CFX_FillRenderOptions::FillType::kEvenOdd) { + return "even-odd"; + } + if (fill_type == CFX_FillRenderOptions::FillType::kWinding) { + return "winding"; + } + CHECK_EQ(fill_type, CFX_FillRenderOptions::FillType::kNoFill); + return "no-fill"; +} + +const char* FormatLineCap(CFX_GraphStateData::LineCap line_cap) { + if (line_cap == CFX_GraphStateData::LineCap::kRound) { + return "round"; + } + if (line_cap == CFX_GraphStateData::LineCap::kSquare) { + return "square"; + } + CHECK_EQ(line_cap, CFX_GraphStateData::LineCap::kButt); + return "butt"; +} + +const char* FormatLineJoin(CFX_GraphStateData::LineJoin line_join) { + if (line_join == CFX_GraphStateData::LineJoin::kRound) { + return "round"; + } + if (line_join == CFX_GraphStateData::LineJoin::kBevel) { + return "bevel"; + } + CHECK_EQ(line_join, CFX_GraphStateData::LineJoin::kMiter); + return "miter"; +} + +} // namespace + +DisplayListDeviceDriver::DisplayListDeviceDriver(int width, int height) + : width_(width), height_(height), clip_box_(0, 0, width, height) {} + +DisplayListDeviceDriver::~DisplayListDeviceDriver() = default; + +DeviceType DisplayListDeviceDriver::GetDeviceType() const { + return DeviceType::kDisplay; +} + +int DisplayListDeviceDriver::GetPixelWidth() const { + return width_; +} + +int DisplayListDeviceDriver::GetPixelHeight() const { + return height_; +} + +int DisplayListDeviceDriver::GetBitsPerPixel() const { + return 32; +} + +bool DisplayListDeviceDriver::RenderCapAlphaOutput() const { + return true; +} + +#if defined(PDF_USE_SKIA) +bool DisplayListDeviceDriver::RenderCapFillStrokePath() const { + return true; +} +#endif + +void DisplayListDeviceDriver::Clear(uint32_t color) { + display_list_ += ByteString::Format("Clear color=0x%08x\n", color); +} + +void DisplayListDeviceDriver::SaveState() { + clip_stack_.push_back(clip_box_); + display_list_ += "SaveState\n"; +} + +void DisplayListDeviceDriver::RestoreState(bool bKeepSaved) { + if (!clip_stack_.empty()) { + clip_box_ = clip_stack_.back(); + if (!bKeepSaved) { + clip_stack_.pop_back(); + } + } + display_list_ += "RestoreState\n"; +} + +void DisplayListDeviceDriver::SetBaseClip(const FX_RECT& rect) { + clip_box_ = rect; + display_list_ += ByteString::Format("SetBaseClip [%d,%d,%d,%d]\n", rect.left, + rect.top, rect.right, rect.bottom); +} + +bool DisplayListDeviceDriver::SetClip_PathFill( + const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_FillRenderOptions& fill_options) { + const char* rule = FormatFillType(fill_options.fill_type); + ByteString mat_str = + pObject2Device ? FormatMatrix(*pObject2Device) : ByteString("none"); + display_list_ += ByteString({"SetClip_PathFill rule=", rule, + " matrix=", mat_str.AsStringView(), " points=[", + FormatPath(path).AsStringView(), "]\n"}); + return true; +} + +bool DisplayListDeviceDriver::SetClip_PathStroke( + const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) { + ByteString mat_str = + pObject2Device ? FormatMatrix(*pObject2Device) : ByteString("none"); + display_list_ += + ByteString({"SetClip_PathStroke matrix=", mat_str.AsStringView(), + " points=[", FormatPath(path).AsStringView(), "]\n"}); + return true; +} + +bool DisplayListDeviceDriver::DrawPath( + const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, + bool group_knockout, + const CFX_FillRenderOptions& fill_options) { + ByteString mat_str = + pObject2Device ? FormatMatrix(*pObject2Device) : ByteString("none"); + ByteString stroke_str; + if (pGraphState) { + const char* cap = FormatLineCap(pGraphState->line_cap()); + const char* join = FormatLineJoin(pGraphState->line_join()); + stroke_str = ByteString::Format("width=%.4f cap=%s join=%s miter=%.4f", + pGraphState->line_width(), cap, join, + pGraphState->miter_limit()); + } else { + stroke_str = "none"; + } + const char* rule = FormatFillType(fill_options.fill_type); + display_list_ += + ByteString({"DrawPath fill=", FormatColor(fill_color).AsStringView(), + " stroke=", FormatColor(stroke_color).AsStringView(), + " rule=", rule, " stroke_state=[", stroke_str.AsStringView(), + "] matrix=", mat_str.AsStringView(), " points=[", + FormatPath(path).AsStringView(), "]\n"}); + return true; +} + +bool DisplayListDeviceDriver::FillRect(const FX_RECT& rect, + uint32_t fill_color) { + display_list_ += + ByteString::Format("FillRect [%d,%d,%d,%d] color=0x%08x\n", rect.left, + rect.top, rect.right, rect.bottom, fill_color); + return true; +} + +bool DisplayListDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo, + const CFX_PointF& ptLineTo, + uint32_t color) { + display_list_ += ByteString::Format( + "DrawCosmeticLine (%.4f,%.4f)->(%.4f,%.4f) color=0x%08x\n", ptMoveTo.x, + ptMoveTo.y, ptLineTo.x, ptLineTo.y, color); + return true; +} + +FX_RECT DisplayListDeviceDriver::GetClipBox() const { + return clip_box_; +} + +bool DisplayListDeviceDriver::SetDIBits(RetainPtr bitmap, + uint32_t color, + const FX_RECT& src_rect, + int left, + int top, + BlendMode blend_type) { + display_list_ += ByteString::Format( + "SetDIBits size=[%d,%d] bpp=%d dest=(%d,%d) src=[%d,%d,%d,%d] " + "color=0x%08x\n", + bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetBPP(), left, top, + src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, color); + return true; +} + +bool DisplayListDeviceDriver::StretchDIBits( + RetainPtr bitmap, + uint32_t color, + int dest_left, + int dest_top, + int dest_width, + int dest_height, + const FX_RECT* pClipRect, + const FXDIB_ResampleOptions& options, + BlendMode blend_type) { + display_list_ += ByteString::Format( + "StretchDIBits size=[%d,%d] bpp=%d dest=[%d,%d,%d,%d] color=0x%08x\n", + bitmap->GetWidth(), bitmap->GetHeight(), bitmap->GetBPP(), dest_left, + dest_top, dest_width, dest_height, color); + return true; +} + +RenderDeviceDriverIface::StartResult DisplayListDeviceDriver::StartDIBits( + RetainPtr bitmap, + float alpha, + uint32_t color, + const CFX_Matrix& matrix, + const FXDIB_ResampleOptions& options, + BlendMode blend_type) { + display_list_ += + ByteString({"StartDIBits ", FormatDIBInfo(*bitmap).AsStringView(), + " matrix=", FormatMatrix(matrix).AsStringView(), + " alpha=", FormatFloat(alpha).AsStringView(), + " color=", FormatColor(color).AsStringView(), "\n"}); + return StartResult(Result::kSuccess, nullptr); +} + +bool DisplayListDeviceDriver::DrawDeviceText( + pdfium::span pCharPos, + CFX_Font* font, + const CFX_Matrix& mtObject2Device, + float font_size, + uint32_t color, + const CFX_TextRenderOptions& options) { + ByteString font_name = font ? font->GetFamilyName() : ByteString(); + if (font_name.IsEmpty() && font) { + font_name = font->GetPsName(); + } + display_list_ += ByteString( + {"DrawDeviceText font=\"", font_name.AsStringView(), + "\" size=", FormatFloat(font_size).AsStringView(), + " color=", FormatColor(color).AsStringView(), + " matrix=", FormatMatrix(mtObject2Device).AsStringView(), " glyphs=["}); + + for (size_t i = 0; i < pCharPos.size(); ++i) { + if (i > 0) { + display_list_ += ' '; + } + display_list_ += FormatGlyph(pCharPos[i]); + } + display_list_ += "]\n"; + return true; +} + +bool DisplayListDeviceDriver::MultiplyAlpha(float alpha) { + display_list_ += ByteString::Format("MultiplyAlpha alpha=%.4f\n", alpha); + return true; +} + +bool DisplayListDeviceDriver::MultiplyAlphaMask( + RetainPtr mask) { + display_list_ += ByteString::Format("MultiplyAlphaMask size=[%d,%d]\n", + mask ? mask->GetWidth() : 0, + mask ? mask->GetHeight() : 0); + return true; +} diff --git a/core/fxge/display_list_device_driver.h b/core/fxge/display_list_device_driver.h new file mode 100644 index 0000000000..9587cc9bf5 --- /dev/null +++ b/core/fxge/display_list_device_driver.h @@ -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. + +#ifndef CORE_FXGE_DISPLAY_LIST_DEVICE_DRIVER_H_ +#define CORE_FXGE_DISPLAY_LIST_DEVICE_DRIVER_H_ + +#include + +#include +#include + +#include "core/fxcrt/bytestring.h" +#include "core/fxcrt/fx_coordinates.h" +#include "core/fxcrt/retain_ptr.h" +#include "core/fxcrt/span.h" +#include "core/fxge/renderdevicedriver_iface.h" + +class CFX_DIBBase; +class CFX_DIBitmap; +class CFX_Font; +class CFX_GraphStateData; +class CFX_Matrix; +class CFX_Path; +struct CFX_FillRenderOptions; +struct CFX_TextRenderOptions; +struct FX_RECT; +struct TextCharPos; + +class DisplayListDeviceDriver final : public RenderDeviceDriverIface { + public: + DisplayListDeviceDriver(int width, int height); + ~DisplayListDeviceDriver() override; + + const ByteString& display_list() const { return display_list_; } + void ClearDisplayList() { display_list_.clear(); } + + // RenderDeviceDriverIface: + DeviceType GetDeviceType() const override; + int GetPixelWidth() const override; + int GetPixelHeight() const override; + int GetBitsPerPixel() const override; + bool RenderCapAlphaOutput() const override; +#if defined(PDF_USE_SKIA) + bool RenderCapFillStrokePath() const override; +#endif + + void Clear(uint32_t color) override; + void SaveState() override; + void RestoreState(bool bKeepSaved) override; + + void SetBaseClip(const FX_RECT& rect) override; + bool SetClip_PathFill(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_FillRenderOptions& fill_options) override; + bool SetClip_PathStroke(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) override; + bool DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, + bool group_knockout, + const CFX_FillRenderOptions& fill_options) override; + bool FillRect(const FX_RECT& rect, uint32_t fill_color) override; + bool DrawCosmeticLine(const CFX_PointF& ptMoveTo, + const CFX_PointF& ptLineTo, + uint32_t color) override; + + FX_RECT GetClipBox() const override; + bool SetDIBits(RetainPtr bitmap, + uint32_t color, + const FX_RECT& src_rect, + int left, + int top, + BlendMode blend_type) override; + bool StretchDIBits(RetainPtr bitmap, + uint32_t color, + int dest_left, + int dest_top, + int dest_width, + int dest_height, + const FX_RECT* pClipRect, + const FXDIB_ResampleOptions& options, + BlendMode blend_type) override; + StartResult StartDIBits(RetainPtr bitmap, + float alpha, + uint32_t color, + const CFX_Matrix& matrix, + const FXDIB_ResampleOptions& options, + BlendMode blend_type) override; + bool DrawDeviceText(pdfium::span pCharPos, + CFX_Font* font, + const CFX_Matrix& mtObject2Device, + float font_size, + uint32_t color, + const CFX_TextRenderOptions& options) override; + bool MultiplyAlpha(float alpha) override; + bool MultiplyAlphaMask(RetainPtr mask) override; + + private: + const int width_; + const int height_; + FX_RECT clip_box_; + std::vector clip_stack_; + ByteString display_list_; +}; + +#endif // CORE_FXGE_DISPLAY_LIST_DEVICE_DRIVER_H_ diff --git a/core/fxge/freetype/fx_freetype.h b/core/fxge/freetype/fx_freetype.h index 2039eb28a1..9f9fd884f2 100644 --- a/core/fxge/freetype/fx_freetype.h +++ b/core/fxge/freetype/fx_freetype.h @@ -7,6 +7,10 @@ #ifndef CORE_FXGE_FREETYPE_FX_FREETYPE_H_ #define CORE_FXGE_FREETYPE_FX_FREETYPE_H_ +#if !defined(PDF_ENABLE_FREETYPE) +#error "PDF_ENABLE_FREETYPE must be defined." +#endif // defined(PDF_ENABLE_FREETYPE) + #include #include diff --git a/core/fxge/fx_font.cpp b/core/fxge/fx_font.cpp index ae6db5e04e..fd19a93b79 100644 --- a/core/fxge/fx_font.cpp +++ b/core/fxge/fx_font.cpp @@ -22,10 +22,6 @@ namespace { -// These numbers come from the OpenType name table specification. -constexpr uint16_t kNameMacEncodingRoman = 0; -constexpr uint16_t kNameWindowsEncodingUnicode = 1; - ByteString GetStringFromTable(pdfium::span string_span, uint16_t offset, uint16_t length) { @@ -127,15 +123,15 @@ ByteString GetNameFromTT(pdfium::span name_table, const uint16_t platform_encoding = fxcrt::GetUInt16MSBFirst(name_table.subspan<2u, 2u>()); - if (platform_identifier == kNamePlatformMac && - platform_encoding == kNameMacEncodingRoman) { + if (platform_identifier == kPlatformMac && + platform_encoding == kMacEncodingRoman) { return GetStringFromTable( string_span, fxcrt::GetUInt16MSBFirst(name_table.subspan<10u, 2u>()), fxcrt::GetUInt16MSBFirst(name_table.subspan<8u, 2u>())); } - if (platform_identifier == kNamePlatformWindows && - platform_encoding == kNameWindowsEncodingUnicode) { + if (platform_identifier == kPlatformWindows && + platform_encoding == kWindowsEncodingUnicode) { // This name is always UTF16-BE and we have to convert it to UTF8. ByteString utf16_be = GetStringFromTable( string_span, diff --git a/core/fxge/fx_font.h b/core/fxge/fx_font.h index b4b017850a..d331062697 100644 --- a/core/fxge/fx_font.h +++ b/core/fxge/fx_font.h @@ -73,10 +73,43 @@ enum class FontAntiAliasingMode : int { kLcd, }; -// These numbers come from the OpenType name table specification. -constexpr uint16_t kNamePlatformAppleUnicode = 0; -constexpr uint16_t kNamePlatformMac = 1; -constexpr uint16_t kNamePlatformWindows = 3; +// OpenType / TrueType platform IDs (used across 'cmap' and 'name' tables). +// Conforms to OpenType specification, with platform 7 as FreeType's Adobe +// convention. +constexpr uint16_t kPlatformAppleUnicode = 0; +constexpr uint16_t kPlatformMac = 1; +constexpr uint16_t kPlatformIso = 2; +constexpr uint16_t kPlatformWindows = 3; +constexpr uint16_t kPlatformCustom = 4; +constexpr uint16_t kPlatformAdobe = 7; + +// OpenType / TrueType encoding IDs for Apple Unicode (kPlatformAppleUnicode). +constexpr uint16_t kAppleUnicodeEncodingUnicode2_0 = 3; + +// OpenType / TrueType encoding IDs for Macintosh (kPlatformMac). +constexpr uint16_t kMacEncodingRoman = 0; + +// OpenType / TrueType encoding IDs for ISO (kPlatformIso). +constexpr uint16_t kIsoEncoding10646 = 1; +constexpr uint16_t kIsoEncoding8859_1 = 2; + +// OpenType / TrueType encoding IDs for Windows (kPlatformWindows). +constexpr uint16_t kWindowsEncodingSymbol = 0; +constexpr uint16_t kWindowsEncodingUnicode = 1; +constexpr uint16_t kWindowsEncodingSjis = 2; +constexpr uint16_t kWindowsEncodingPrc = 3; +constexpr uint16_t kWindowsEncodingGb2312 = kWindowsEncodingPrc; +constexpr uint16_t kWindowsEncodingBig5 = 4; +constexpr uint16_t kWindowsEncodingWansung = 5; +constexpr uint16_t kWindowsEncodingJohab = 6; +constexpr uint16_t kWindowsEncodingUcs4 = 10; + +// Encoding IDs for Adobe platform (kPlatformAdobe, FreeType Type 1 / CFF +// convention). +constexpr uint16_t kAdobeEncodingStandard = 0; +constexpr uint16_t kAdobeEncodingExpert = 1; +constexpr uint16_t kAdobeEncodingCustom = 2; +constexpr uint16_t kAdobeEncodingLatin1 = 3; // The length of the font subset prefix, as defined in ISO 32000-1:2008 spec, // section 9.6.4 "Font Subsets". diff --git a/core/fxge/renderdevicedriver_iface.cpp b/core/fxge/renderdevicedriver_iface.cpp index f3ecba417f..f9523853b1 100644 --- a/core/fxge/renderdevicedriver_iface.cpp +++ b/core/fxge/renderdevicedriver_iface.cpp @@ -129,8 +129,6 @@ bool RenderDeviceDriverIface::SetBitsWithMask( return false; } -void RenderDeviceDriverIface::SetGroupKnockout(bool group_knockout) {} - void RenderDeviceDriverIface::SyncInternalBitmaps() {} #endif // defined(PDF_USE_SKIA) diff --git a/core/fxge/renderdevicedriver_iface.h b/core/fxge/renderdevicedriver_iface.h index 4c876a6ad0..df0738362f 100644 --- a/core/fxge/renderdevicedriver_iface.h +++ b/core/fxge/renderdevicedriver_iface.h @@ -99,6 +99,7 @@ class RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) = 0; virtual bool FillRect(const FX_RECT& rect, uint32_t fill_color); virtual bool DrawCosmeticLine(const CFX_PointF& ptMoveTo, @@ -152,7 +153,6 @@ class RenderDeviceDriverIface { int top, float alpha, BlendMode blend_type); - virtual void SetGroupKnockout(bool group_knockout); // For `CFX_SkiaDeviceDriver` only: // Syncs the current rendering result from the internal buffer to the output diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp index 28b16c53fa..144d7469b2 100644 --- a/core/fxge/skia/fx_skia_device.cpp +++ b/core/fxge/skia/fx_skia_device.cpp @@ -664,11 +664,9 @@ SkFont SkFontFromCFXFont(const CFX_Font* cfx_font, std::unique_ptr CFX_SkiaDeviceDriver::Create( RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) { - auto driver = pdfium::WrapUnique( - new CFX_SkiaDeviceDriver(std::move(pBitmap), bRgbByteOrder, - std::move(pBackdropBitmap), bGroupKnockout)); + RetainPtr pBackdropBitmap) { + auto driver = pdfium::WrapUnique(new CFX_SkiaDeviceDriver( + std::move(pBitmap), bRgbByteOrder, std::move(pBackdropBitmap))); if (!driver->canvas_) { return nullptr; } @@ -690,12 +688,10 @@ std::unique_ptr CFX_SkiaDeviceDriver::Create( CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver( RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) + RetainPtr pBackdropBitmap) : bitmap_(std::move(pBitmap)), backdrop_bitmap_(pBackdropBitmap), - rgb_byte_order_(bRgbByteOrder), - group_knockout_(bGroupKnockout) { + rgb_byte_order_(bRgbByteOrder) { SkColorType color_type; const int bpp = bitmap_->GetBPP(); SkAlphaType alpha_type = kPremul_SkAlphaType; @@ -745,7 +741,7 @@ CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver( } CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkCanvas& canvas) - : canvas_(&canvas), rgb_byte_order_(false), group_knockout_(false) { + : canvas_(&canvas), rgb_byte_order_(false) { int width = canvas_->imageInfo().width(); int height = canvas_->imageInfo().height(); DCHECK_EQ(kUnknown_SkColorType, canvas_->imageInfo().colorType()); @@ -1142,6 +1138,7 @@ bool CFX_SkiaDeviceDriver::DrawPath(const CFX_Path& cfx_path, const CFX_GraphStateData* stroke_options, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) { fill_options_ = fill_options; @@ -1179,7 +1176,7 @@ bool CFX_SkiaDeviceDriver::DrawPath(const CFX_Path& cfx_path, // See section 11.4.6 of in ISO 32000-1:2008: // "At any given point, only the topmost object enclosing the point shall // contribute to the result colour and opacity of the group as a whole" - if (stroke_alpha && group_knockout_) { + if (stroke_alpha && group_knockout) { // Draw the knockout group path on a separate layer so blend modes can be // adjusted. When restore() is called path_paint is used to composite the // layer. @@ -1601,10 +1598,6 @@ bool CFX_SkiaDeviceDriver::SetBitsWithMask(RetainPtr bitmap, blend_type); } -void CFX_SkiaDeviceDriver::SetGroupKnockout(bool group_knockout) { - group_knockout_ = group_knockout; -} - void CFX_SkiaDeviceDriver::SyncInternalBitmaps() { if (!original_bitmap_) { return; @@ -1689,17 +1682,11 @@ CFX_SkiaDeviceDriver::CharDetail::~CharDetail() = default; bool CFX_RenderDevice::AttachSkiaImpl(RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout) { - // FPDF_FFLDrawSkia() ends up calling this method with a deliberately null - // `pBitmap`. - if (!pBitmap) { - return false; - } + RetainPtr pBackdropBitmap) { + CHECK(pBitmap); SetBitmap(pBitmap); - auto driver = - CFX_SkiaDeviceDriver::Create(std::move(pBitmap), bRgbByteOrder, - std::move(pBackdropBitmap), bGroupKnockout); + auto driver = CFX_SkiaDeviceDriver::Create(std::move(pBitmap), bRgbByteOrder, + std::move(pBackdropBitmap)); if (!driver) { return false; } @@ -1708,31 +1695,3 @@ bool CFX_RenderDevice::AttachSkiaImpl(RetainPtr pBitmap, return true; } -bool CFX_RenderDevice::AttachCanvas(SkCanvas& canvas) { - auto driver = CFX_SkiaDeviceDriver::Create(canvas); - if (!driver) { - return false; - } - SetDeviceDriver(std::move(driver)); - return true; -} - -bool CFX_RenderDevice::CreateSkia(int width, - int height, - FXDIB_Format format, - RetainPtr pBackdropBitmap) { - auto pBitmap = pdfium::MakeRetain(); - if (!pBitmap->Create(width, height, format)) { - return false; - } - - SetBitmap(pBitmap); - auto driver = CFX_SkiaDeviceDriver::Create(std::move(pBitmap), false, - std::move(pBackdropBitmap), false); - if (!driver) { - return false; - } - - SetDeviceDriver(std::move(driver)); - return true; -} diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h index 05921028f7..e29f76876d 100644 --- a/core/fxge/skia/fx_skia_device.h +++ b/core/fxge/skia/fx_skia_device.h @@ -42,8 +42,7 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { static std::unique_ptr Create( RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); + RetainPtr pBackdropBitmap); static std::unique_ptr Create(SkCanvas& canvas); ~CFX_SkiaDeviceDriver() override; @@ -75,6 +74,7 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) override; bool FillRect(const FX_RECT& rect, uint32_t fill_color) override; FX_RECT GetClipBox() const override; @@ -94,7 +94,6 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { int dest_top, float alpha, BlendMode blend_type) override; - void SetGroupKnockout(bool group_knockout) override; void SyncInternalBitmaps() override; bool StretchDIBits(RetainPtr bitmap, uint32_t color, @@ -171,8 +170,7 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { // Use the public creation methods instead. CFX_SkiaDeviceDriver(RetainPtr pBitmap, bool bRgbByteOrder, - RetainPtr pBackdropBitmap, - bool bGroupKnockout); + RetainPtr pBackdropBitmap); explicit CFX_SkiaDeviceDriver(SkCanvas& canvas); bool TryDrawText(pdfium::span char_pos, @@ -213,9 +211,8 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { UnownedPtr canvas_; CFX_FillRenderOptions fill_options_; const bool rgb_byte_order_; - bool group_knockout_; - CharDetail char_details_; + // accumulator for txt rotate/scale/translate DataVector rsxform_; }; diff --git a/core/fxge/skia/fx_skia_device_embeddertest.cpp b/core/fxge/skia/fx_skia_device_embeddertest.cpp index c159df3c2d..26c8a9d272 100644 --- a/core/fxge/skia/fx_skia_device_embeddertest.cpp +++ b/core/fxge/skia/fx_skia_device_embeddertest.cpp @@ -95,6 +95,7 @@ void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) { } if (state.graphic_ == State::Graphic::kPath) { driver->DrawPath(path1, &matrix, &graphState, 0xFF112233, 0, + /*group_knockout=*/false, CFX_FillRenderOptions::WindingOptions()); } else if (state.graphic_ == State::Graphic::kText) { driver->DrawDeviceText(pdfium::span_from_ref(charPos), &font, matrix, @@ -121,6 +122,7 @@ void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) { } if (state.graphic_ == State::Graphic::kPath) { driver->DrawPath(path2, &matrix2, &graphState, 0xFF112233, 0, + /*group_knockout=*/false, CFX_FillRenderOptions::WindingOptions()); } else if (state.graphic_ == State::Graphic::kText) { driver->DrawDeviceText(pdfium::span_from_ref(charPos), &font, matrix2, @@ -162,7 +164,7 @@ void Harness(void (*Test)(CFX_SkiaDeviceDriver*, const State&), ASSERT_TRUE( FPDFBitmap_FillRect(bitmap.get(), 0, 0, kWidth, kHeight, 0x00000000)); RetainPtr pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap.get())); - auto driver = CFX_SkiaDeviceDriver::Create(pBitmap, false, nullptr, false); + auto driver = CFX_SkiaDeviceDriver::Create(pBitmap, false, nullptr); ASSERT_TRUE(driver); (*Test)(driver.get(), state); uint32_t pixel = pBitmap->GetPixelForTesting(0, 0); diff --git a/core/fxge/skrifa/fx_skrifa_unittest.cpp b/core/fxge/skrifa/fx_skrifa_unittest.cpp index a018616b08..c549e5e0bc 100644 --- a/core/fxge/skrifa/fx_skrifa_unittest.cpp +++ b/core/fxge/skrifa/fx_skrifa_unittest.cpp @@ -236,6 +236,7 @@ TEST(FxSkrifaTest, TestType1Face) { ASSERT_TRUE(path); EXPECT_EQ(path->GetPoints().size(), 12u); +#if defined(PDF_ENABLE_FREETYPE) auto glyph_bitmap = face->RenderGlyph(gid_x, /*is_cid_font=*/false, /*is_vertical=*/false, CFX_Matrix(12.0f, 0, 0, 12.0f, 0, 0), /*dest_width=*/0, @@ -261,7 +262,42 @@ TEST(FxSkrifaTest, TestType1Face) { ASSERT_TRUE(glyph_mono); EXPECT_TRUE(glyph_mono->GetBitmap()); EXPECT_EQ(glyph_mono->GetBitmap()->GetWidth(), 6); - EXPECT_EQ(glyph_mono->GetBitmap()->GetHeight(), 7); + EXPECT_EQ(glyph_mono->GetBitmap()->GetHeight(), 6); +#endif // defined(PDF_ENABLE_FREETYPE) +} + +TEST(FxSkrifaTest, TestBareCffFace) { + if (!CFX_GEModule::IsFontations()) { + GTEST_SKIP(); + } + + auto stream = pdfium::MakeRetain(kFoxitSansFontData); + auto face = CFX_Face::New(nullptr, stream, 0); + ASSERT_TRUE(face); + + EXPECT_EQ(face->GetFontFormat(), "CFF"); + EXPECT_FALSE(face->IsTtOt()); + EXPECT_EQ(face->GetCharMapCount(), 2u); + EXPECT_EQ(face->GetCharMapPlatformIdByIndex(0), kPlatformAppleUnicode); + EXPECT_EQ(face->GetCharMapIdPairByIndex(0).encoding_id, + kAppleUnicodeEncodingUnicode2_0); + EXPECT_EQ(face->GetCharMapEncodingByIndex(0), fxge::FontEncoding::kUnicode); + EXPECT_EQ(face->GetCharMapPlatformIdByIndex(1), kPlatformAdobe); + EXPECT_EQ(face->GetCharMapIdPairByIndex(1).encoding_id, kAdobeEncodingCustom); + EXPECT_EQ(face->GetCharMapEncodingByIndex(1), + fxge::FontEncoding::kAdobeCustom); + + EXPECT_TRUE(face->SelectCharMap(fxge::FontEncoding::kAdobeCustom)); + int gid_x = face->GetCharIndex(120); + EXPECT_EQ(gid_x, 222); + + EXPECT_TRUE(face->SelectCharMap(fxge::FontEncoding::kUnicode)); + EXPECT_EQ(face->GetCharIndex(L'x'), gid_x); + + std::unique_ptr path = + face->LoadGlyphPath(gid_x, 0, false, nullptr); + ASSERT_TRUE(path); + EXPECT_EQ(path->GetPoints().size(), 12u); } TEST(FxSkrifaTest, TestGetSfntTable) { @@ -285,3 +321,35 @@ TEST(FxSkrifaTest, TestGetSfntTable) { constexpr uint32_t kMissingTag = 0x78797a77; EXPECT_EQ(0u, font->get_sfnt_table(kMissingTag, rust::Slice())); } + +TEST(FxSkrifaTest, TestCharmaps) { + std::string font_path = PathService::GetTestFilePath("fonts/ahem/Ahem.ttf"); + std::vector bytes = GetFileContents(font_path.c_str()); + + auto font = skrifa::new_font(rust::Slice(bytes), 0); + ASSERT_TRUE(font->is_ok()); + + size_t count = font->get_charmap_count(); + EXPECT_EQ(count, 2u); + + // Verify charmap info and map characters to glyph IDs. + size_t found_unicode_times = 0; + for (size_t i = 0; i < count; ++i) { + skrifa::CharMapInfo info; + EXPECT_TRUE(font->get_charmap_info(i, info)); + if (info.platform_id == kPlatformWindows && + info.encoding_id == kWindowsEncodingUnicode) { + ++found_unicode_times; + // In Ahem, space (32) maps to glyph ID 3. + EXPECT_EQ(3u, font->cmap_char_to_gid(i, 32)); + // 'A' (65) should map to non-zero glyph ID. + EXPECT_EQ(35u, font->cmap_char_to_gid(i, 65)); + } + } + EXPECT_EQ(found_unicode_times, 1u); + + // Out-of-bounds queries. + skrifa::CharMapInfo dummy_info; + EXPECT_FALSE(font->get_charmap_info(count, dummy_info)); + EXPECT_EQ(0u, font->cmap_char_to_gid(count, 32)); +} diff --git a/core/fxge/skrifa/src/main.rs b/core/fxge/skrifa/src/main.rs index 51591d8d46..d6ad384626 100644 --- a/core/fxge/skrifa/src/main.rs +++ b/core/fxge/skrifa/src/main.rs @@ -15,7 +15,7 @@ use read_fonts::{ string::Sid, type1::Type1Font, }, - tables::{head::MacStyle, os2::SelectionFlags}, + tables::{cmap::Cmap, head::MacStyle, os2::SelectionFlags}, types::{Fixed, GlyphId, Tag}, TableProvider, }; @@ -104,6 +104,12 @@ mod skrifa_ffi { pub glyph_index: u32, } + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub struct CharMapInfo { + pub platform_id: u16, + pub encoding_id: u16, + } + #[derive(Clone, Debug)] pub struct Outline { pub verbs: Vec, @@ -156,6 +162,9 @@ mod skrifa_ffi { fn glyph_bounds(&self, glyph_index: u32) -> BoundingBox; fn get_font_bbox(&self, bbox: &mut BoundingBox) -> bool; fn get_sfnt_table(&self, table: u32, buffer: &mut [u8]) -> usize; + fn get_charmap_count(&self) -> usize; + fn get_charmap_info(&self, index: usize, info: &mut CharMapInfo) -> bool; + fn cmap_char_to_gid(&self, subtable_index: usize, code: u32) -> u32; fn agl_name_to_unicode(name: &str, unicode: &mut u32) -> bool; fn agl_unicode_to_name(unicode: u32, name: &mut [u8]) -> &[u8]; @@ -170,8 +179,8 @@ mod skrifa_ffi { } use skrifa_ffi::{ - BoundingBox, CharCodeAndIndex, CodePageRange, FaceFormat, Os2Panose, Outline, PathVerb, Point, - PsEncodingKind, UnicodeRange, + BoundingBox, CharCodeAndIndex, CharMapInfo, CodePageRange, FaceFormat, Os2Panose, Outline, + PathVerb, Point, PsEncodingKind, UnicodeRange, }; #[allow(clippy::large_enum_variant)] @@ -189,6 +198,7 @@ struct HinterCache { pub struct Sfnt<'a> { font: FontRef<'a>, + cmap: Option>, metrics: Metrics, ps_name: Option, family_name: Option, @@ -203,6 +213,7 @@ pub struct Sfnt<'a> { impl<'a> Sfnt<'a> { fn new(data: &'a [u8], index: u32) -> Option { let font = FontRef::from_index(data, index).ok()?; + let cmap = font.cmap().ok(); let metrics = font.metrics(Size::unscaled(), LocationRef::default()); let get_name = |id| font.localized_strings(id).english_or_first().map(|s| s.to_string()); let ps_name = get_name(StringId::POSTSCRIPT_NAME); @@ -216,6 +227,7 @@ impl<'a> Sfnt<'a> { let is_tricky = outlines.require_interpreter(); Some(Self { font, + cmap, ps_name, metrics, family_name, @@ -865,6 +877,49 @@ impl SkrifaFont<'_> { buffer.len() } + fn get_charmap_count(&self) -> usize { + let Self::Sfnt(sfnt) = self else { + return 0; + }; + sfnt.cmap.as_ref().map(|c| c.encoding_records().len()).unwrap_or(0) + } + + fn get_charmap_info(&self, index: usize, info: &mut CharMapInfo) -> bool { + let Self::Sfnt(sfnt) = self else { + return false; + }; + let Some(cmap) = &sfnt.cmap else { + return false; + }; + let Some(record) = cmap.encoding_records().get(index) else { + return false; + }; + info.platform_id = record.platform_id() as u16; + info.encoding_id = record.encoding_id(); + true + } + + fn cmap_char_to_gid(&self, subtable_index: usize, code: u32) -> u32 { + let Self::Sfnt(sfnt) = self else { + return 0; + }; + let Some(cmap) = &sfnt.cmap else { + return 0; + }; + let Some(record) = cmap.encoding_records().get(subtable_index) else { + return 0; + }; + let Ok(subtable) = record.subtable(cmap.offset_data()) else { + return 0; + }; + let gid = subtable.map_codepoint(code).map(|g| g.to_u32()).unwrap_or(0); + if gid < self.num_glyphs() { + gid + } else { + 0 + } + } + fn is_fixed_pitch(&self) -> bool { match self { Self::Sfnt(sfnt) => sfnt.metrics.is_monospace, diff --git a/core/fxge/skrifa/src/outlines.h b/core/fxge/skrifa/src/outlines.h index dcfe923907..58f3c7b9c7 100644 --- a/core/fxge/skrifa/src/outlines.h +++ b/core/fxge/skrifa/src/outlines.h @@ -5,7 +5,7 @@ // Based on https://github.com/googlefonts/fontations/pull/1820 #pragma once -#include "third_party/rust/cxx/v1/cxx.h" +#include "third_party/rust/chromium_crates_io/vendor/cxx-v1/include/cxx.h" namespace skrifa { void run(rust::Str font_path); } diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp index ccd63f4789..74ff44fc25 100644 --- a/core/fxge/win32/cfx_psrenderer.cpp +++ b/core/fxge/win32/cfx_psrenderer.cpp @@ -184,13 +184,11 @@ ByteString GenerateType42FontData(const CFX_Font* font) { return ByteString(); } - ByteString output = "%%BeginResource: font "; - output += psname; - output += "\n"; - output += sfnt_data.value(); - output += GenerateType42FontDictionary(psname, font->GetRawBBox().value(), - num_glyphs, kGlyphsPerDescendantFont); - return output; + ByteString font_dict = GenerateType42FontDictionary( + psname, font->GetRawBBox().value(), num_glyphs, kGlyphsPerDescendantFont); + return ByteString({"%%BeginResource: font ", psname.AsStringView(), '\n', + sfnt_data.value().AsStringView(), + font_dict.AsStringView()}); } } // namespace diff --git a/core/fxge/win32/cgdi_device_driver.cpp b/core/fxge/win32/cgdi_device_driver.cpp index 0ac0c5cb11..504e0ad7b2 100644 --- a/core/fxge/win32/cgdi_device_driver.cpp +++ b/core/fxge/win32/cgdi_device_driver.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include "core/fxcrt/check.h" @@ -26,6 +27,10 @@ #include "core/fxge/cfx_path.h" #include "core/fxge/dib/cfx_dibbase.h" #include "core/fxge/dib/cfx_dibitmap.h" +#include "core/fxge/win32/cgdi_display_driver.h" +#include "core/fxge/win32/cgdi_printer_driver.h" +#include "core/fxge/win32/cps_printer_driver.h" +#include "core/fxge/win32/ctext_only_printer_driver.h" #include "core/fxge/win32/cwin32_platform.h" #if defined(PDF_USE_AGG) @@ -108,8 +113,8 @@ HBRUSH CreateBrush(uint32_t argb) { return CreateSolidBrush(ArgbToColorRef(argb)); } -void SetPathToDC(HDC hDC, const CFX_Path& path, const CFX_Matrix* pMatrix) { - BeginPath(hDC); +void SetPathToDC(HDC hdc, const CFX_Path& path, const CFX_Matrix* pMatrix) { + BeginPath(hdc); pdfium::span points = path.GetPoints(); for (size_t i = 0; i < points.size(); ++i) { @@ -121,13 +126,13 @@ void SetPathToDC(HDC hDC, const CFX_Path& path, const CFX_Matrix* pMatrix) { CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y)); CFX_Path::Point::Type point_type = points[i].type_; if (point_type == CFX_Path::Point::Type::kMove) { - MoveToEx(hDC, screen.x, screen.y, nullptr); + MoveToEx(hdc, screen.x, screen.y, nullptr); } else if (point_type == CFX_Path::Point::Type::kLine) { if (points[i].point_ == points[i - 1].point_) { screen.x++; } - LineTo(hDC, screen.x, screen.y); + LineTo(hdc, screen.x, screen.y); } else if (point_type == CFX_Path::Point::Type::kBezier) { POINT lppt[3]; lppt[0].x = screen.x; @@ -148,14 +153,14 @@ void SetPathToDC(HDC hDC, const CFX_Path& path, const CFX_Matrix* pMatrix) { lppt[2].x = FXSYS_roundf(pos.x); lppt[2].y = FXSYS_roundf(pos.y); - PolyBezierTo(hDC, lppt, 3); + PolyBezierTo(hdc, lppt, 3); i += 2; } if (points[i].close_figure_) { - CloseFigure(hDC); + CloseFigure(hdc); } } - EndPath(hDC); + EndPath(hdc); } FixedSizeDataVector GetBitmapInfoHeader( @@ -348,14 +353,42 @@ unsigned LineClip(float w, pdfium::agg::rect_base rect(0.0f, 0.0f, w, h); return pdfium::agg::clip_liang_barsky(x1, y1, x2, y2, rect, x, y); #else - return 0u; + NOTREACHED(); #endif } } // namespace -CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type) - : dc_handle_(hDC), device_type_(device_type) { +// static +std::unique_ptr CGdiDeviceDriver::CreateDriver( + HDC hdc, + CFX_PSFontTracker* ps_font_tracker) { + int device_type = ::GetDeviceCaps(hdc, TECHNOLOGY); + int obj_type = ::GetObjectType(hdc); + const bool use_printer = + device_type == DT_RASPRINTER || device_type == DT_PLOTTER || + device_type == DT_CHARSTREAM || obj_type == OBJ_ENHMETADC; + + if (!use_printer) { + return std::make_unique(hdc); + } + + WindowsPrintMode print_mode = CFX_GEModule::Get()->GetPrintMode(); + if (print_mode == WindowsPrintMode::kEmf || + print_mode == WindowsPrintMode::kEmfImageMasks) { + return std::make_unique(hdc); + } + + if (print_mode == WindowsPrintMode::kTextOnly) { + return std::make_unique(hdc); + } + + return std::make_unique( + hdc, print_mode, ps_font_tracker, CFX_GEModule::Get()->GetEncoderIface()); +} + +CGdiDeviceDriver::CGdiDeviceDriver(HDC hdc, DeviceType device_type) + : dc_handle_(hdc), device_type_(device_type) { SetStretchBltMode(dc_handle_, HALFTONE); DWORD obj_type = GetObjectType(dc_handle_); metafile_dctype_ = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE; @@ -623,6 +656,7 @@ bool CGdiDeviceDriver::DrawPath(const CFX_Path& path, const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) { auto* pPlatform = static_cast(CFX_GEModule::Get()->GetPlatform()); diff --git a/core/fxge/win32/cgdi_device_driver.h b/core/fxge/win32/cgdi_device_driver.h index e350333ceb..a3cd39f983 100644 --- a/core/fxge/win32/cgdi_device_driver.h +++ b/core/fxge/win32/cgdi_device_driver.h @@ -9,16 +9,23 @@ #include +#include #include #include "core/fxcrt/retain_ptr.h" #include "core/fxge/renderdevicedriver_iface.h" class CFX_DIBBase; +class CFX_PSFontTracker; class CGdiDeviceDriver : public RenderDeviceDriverIface { + public: + static std::unique_ptr CreateDriver( + HDC hdc, + CFX_PSFontTracker* ps_font_tracker); + protected: - CGdiDeviceDriver(HDC hDC, DeviceType device_type); + CGdiDeviceDriver(HDC hdc, DeviceType device_type); ~CGdiDeviceDriver() override; // RenderDeviceDriverIface: @@ -43,6 +50,7 @@ class CGdiDeviceDriver : public RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) override; bool FillRect(const FX_RECT& rect, uint32_t fill_color) override; bool DrawCosmeticLine(const CFX_PointF& ptMoveTo, diff --git a/core/fxge/win32/cps_printer_driver.cpp b/core/fxge/win32/cps_printer_driver.cpp index cb83206349..625dd79e6b 100644 --- a/core/fxge/win32/cps_printer_driver.cpp +++ b/core/fxge/win32/cps_printer_driver.cpp @@ -149,6 +149,7 @@ bool CPSPrinterDriver::DrawPath(const CFX_Path& path, const CFX_GraphStateData* pGraphState, FX_ARGB fill_color, FX_ARGB stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) { return psrenderer_.DrawPath(path, pObject2Device, pGraphState, fill_color, stroke_color, fill_options); diff --git a/core/fxge/win32/cps_printer_driver.h b/core/fxge/win32/cps_printer_driver.h index 85fbfb56fb..565a71f75e 100644 --- a/core/fxge/win32/cps_printer_driver.h +++ b/core/fxge/win32/cps_printer_driver.h @@ -47,6 +47,7 @@ class CPSPrinterDriver final : public RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) override; FX_RECT GetClipBox() const override; bool SetDIBits(RetainPtr bitmap, diff --git a/core/fxge/win32/ctext_only_printer_driver.cpp b/core/fxge/win32/ctext_only_printer_driver.cpp index 957a46e7b0..b7eed77ca2 100644 --- a/core/fxge/win32/ctext_only_printer_driver.cpp +++ b/core/fxge/win32/ctext_only_printer_driver.cpp @@ -76,6 +76,7 @@ bool CTextOnlyPrinterDriver::DrawPath( const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) { return false; } diff --git a/core/fxge/win32/ctext_only_printer_driver.h b/core/fxge/win32/ctext_only_printer_driver.h index 5b7038f724..00c6212603 100644 --- a/core/fxge/win32/ctext_only_printer_driver.h +++ b/core/fxge/win32/ctext_only_printer_driver.h @@ -39,6 +39,7 @@ class CTextOnlyPrinterDriver final : public RenderDeviceDriverIface { const CFX_GraphStateData* pGraphState, uint32_t fill_color, uint32_t stroke_color, + bool group_knockout, const CFX_FillRenderOptions& fill_options) override; FX_RECT GetClipBox() const override; bool SetDIBits(RetainPtr bitmap, diff --git a/docs/embedpdf/syncs/2026-10-13-06fbf69b3.md b/docs/embedpdf/syncs/2026-10-13-06fbf69b3.md new file mode 100644 index 0000000000..66c98ad009 --- /dev/null +++ b/docs/embedpdf/syncs/2026-10-13-06fbf69b3.md @@ -0,0 +1,155 @@ +# Sync 2026-10-13: upstream 3c9748bd6..06fbf69b3 (71 commits) + +## Summary + +Upstream from 2026-10-01 to 2026-10-10, merged into `c41fec2c5`. + +Upstream made FreeType optional (for Fontations and Skia builds), started using libyuv for pixel +conversions, stopped generating appearances for closed popups (the leak our reads-dont-write +change already fixed), moved group knockout from the device constructors to `DrawPath()`, and gave +`CFX_RenderDevice::CreateForBitmap()` a backdrop parameter. We put our FreeType-only font code +under upstream's FreeType guard, took upstream's popup fix and dropped ours, and ported our render +paths and AGG path memo to the new device API. + +## Likely security fixes + +- `686814d06` 2026-10-09 Avoid integer overflows in FaxG4GetRow() +- `f51279699` 2026-10-09 Avoid nullptr crash in CXFA_FFField::SetEditScrollOffset() +- `e2a8e0979` 2026-10-08 Restore implicit public headers in fuzzer impl targets + +`686814d06` is in code we build (the CCITT fax decoder); we never changed that file, so we have +upstream's fix as is. `f51279699` is XFA (off for us). `e2a8e0979` restores fuzzer build targets. + +## Conflicts (8 files, 10 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 | +| :-- | --: | :-- | --: | :-- | :-- | +| `BUILD.gn` | 1 | untagged | **1** | `8d804d142` Fix check_includes_strict violations in pdfium.; `e72afa79f` Support building PDFium and running tests without FreeType; `88d0b7025` Use libyuv::RGB24ToRAW in fxcodec::ReverseRGB | Both: our `EPDF_THREAD_LOCAL_GLOBALS` define, kept as it was, and upstream's `PDF_USE_LIBYUV` define (`88d0b7025`). | +| `core/fpdfdoc/cpdf_annotlist_unittest.cpp` | 1 | untagged | | `e38c84159` Add CFX_RenderDevice::CreateWithDriver(); `4fa235012` Defer popup appearance generation until popup is opened; `f6289ff04` Add regression tests for popup appearance generation | Both: our `ConstructionPreservesDirectAnnotations` and upstream's popup tests. One upstream expectation changed: building the list leaves the text annotation without `/AP`, because its appearance is drawn from memory (reads-dont-write, tagged); upstream expects one written to the file. | +| `core/fxge/BUILD.gn` | 1 | untagged | | `b4f594217` Add test-only DisplayListDeviceDriver and use in EmbedderTest; `8d804d142` Fix check_includes_strict violations in pdfium.; `e72afa79f` Support building PDFium and running tests without FreeType; +2 more | Upstream's `fxcrt` as a public dependency and its abseil dependency, with our HarfBuzz and `fdrm` dependencies. | +| `core/fxge/agg/cfx_agg_devicedriver.cpp` | 2 | render-speedup | | `2b23f4de3` Enforce non-null bitmap in CFX_RenderDevice::AttachSkiaImpl(); `e38c84159` Add CFX_RenderDevice::CreateWithDriver(); `e644f8cba` Remove group_knockout argument from device class constructors. | Ours: the path memo, workspace and `DrawPathPasses()` (render-speedup). Upstream moved group knockout from a driver member to a `DrawPath()` argument (`e644f8cba`), so `DrawPathPasses()` takes it as an argument and hands it to the stroke pass, as upstream's `DrawPath()` does. The fill pass still passes `false`, as upstream. | +| `core/fxge/cfx_face.cpp` | 1 | substitute-font-bounds | | `29b324685` Stop using third_party/rust/cxx/v1/cxx.h; `e72afa79f` Support building PDFium and running tests without FreeType; `3670b03f2` Support bare CFF fonts in CFX_Face via Skrifa; +4 more | Upstream's `PDF_ENABLE_FREETYPE` guard in `GetGlyphWidth()` (`e72afa79f`) with our scoped variation inside it. `ScopedFaceVariation` and the variation part of `GetDrawnGlyphBBox()` call FreeType, so they got the same guard. | +| `fpdfsdk/BUILD.gn` | 1 | untagged | | `b4f594217` Add test-only DisplayListDeviceDriver and use in EmbedderTest; `8d804d142` Fix check_includes_strict violations in pdfium. | Upstream's split into `public_deps` and `deps` (`8d804d142`); our `//third_party:png` goes in `deps`. | +| `fpdfsdk/fpdf_annot_embeddertest.cpp` | 2 | untagged | | `4fa235012` Defer popup appearance generation until popup is opened | Both: upstream's minimum-size check, and our check that the saved copy renders the same. Upstream's `Bug1206` now expects equal sizes across renders, as ours already did. | +| `third_party/agg23/README.pdfium` | 1 | untagged | | `58e113057` Correctly initialize current cell in agg::outline_aa | Both: upstream's new `0018-outline_aa_reset.patch`, then ours, renamed `0019-reuse-across-paths.patch` and regenerated (same lines). Our `reset_to_initial()` calls `reset()`, so it gets upstream's fix. | + +## Write audit (1 line) + +Verdicts: `fresh` (created right there), `door` (through the copy-up door or its owner), `fixed` (broke a rule; fixed in this sync, with a tag), `not-built`. + +| File:line | What | Verdict | Why | +| :-- | :-- | :-- | :-- | +| `fpdfsdk/display_list_renderer.cpp:76` | GetOrCreatePageView | not-built | Test support (`testonly`, `b4f594217`), not in the library. Not a PDF object: a page view, as in `FPDF_FFLDraw()`. | + +## Upstream changes outside the conflicts + +- **Closed popups get no appearance (`4fa235012`).** Upstream's `ShouldGenerateAP()` now + returns false for a closed popup, so building an annotation list writes nothing for it. That is + the leak our reads-dont-write change fixed in `CreatePopupAnnot()` (the popup was made with + `MissingAppearance::kGenerateWhenDrawn`). Ours is dropped; upstream's code does the same. The + enum stays: `EPDF_RenderAnnotBitmap()`, `EPDF_RenderAnnotBitmapUnrotated()` and + `EPDFAnnot_GetDrawingRect()` still use it. Upstream's new popup tests pass. +- **`CFX_RenderDevice::CreateForBitmap(bitmap, backdrop, rgb_byte_order)` (`e644f8cba`).** The byte + order moved to the third argument, so our two-argument calls passed it as the backdrop. Ported + in `EPDF_RenderPageBitmapWithMatrix_Start()`, `EPDF_RenderAnnotBitmap()` and + `EPDF_RenderAnnotBitmapUnrotated()`, as upstream's own callers. +- **FreeType optional (`e72afa79f`).** `fxcrt` no longer passes FreeType's include path to every + target. Our base and layer documents (in `parser`) make their page and render data, whose font + headers need it, so `parser` now depends on `fxge` (tagged in `parser/BUILD.gn`; skipped with + Skia, where upstream already adds it). Our builds keep FreeType; the fork's own font code + (`GetFsTypeFlags()`, CID-keyed CFF, the font registry) needs it. +- **libyuv (`88d0b7025`, `c585d039f`).** On by default (`pdf_use_libyuv = true`): RGB byte + swapping and BGR-to-BGRA compositing go through libyuv. A new dependency for every target, wasm + included. +- **Rectangles with extra points (`321be12a4`, `97ab661d8`).** A rectangle path with a point in + the middle of an edge is still a rectangle, and is written as `re` with the right corner. Only + upstream's call sites write `re`. +- **Text page ignores non-finite objects (`06fbf69b3`)**, **old-style named destinations + dereferenced (`e0dd7ca09`)**: no overlap with ours. +- **Stretch engine and 24-bit decode (`aeb3d6e3d`, `bd9ff698b`, `68b540df6`)** sit next to our + render-speedup row loop and decoded-image-store code, without overlap. + +## Upstream appearance generator + +Our generator is separate (upstream-sync plan §6.2): port each commit or skip it. + +| Commit | Subject | Port or skip | Why | +| :-- | :-- | :-- | :-- | +| `ceea6879c` | Use ByteString initializer list constructor for simple substitutions | skip | Style only, same output. Our copy of `GetPopupContentsString()` (`cpdf_appearancegenerator_icons.cpp`) keeps `+=`. | + +## Globals + +The heuristic's `sStream` in `fpdf_flatten.cpp` is a local variable. `check-globals.py` on the +darwin-arm64 release build found one new shared global, libyuv's `cpu_info_` (CPU features, +detected on first use). libyuv reads and writes it with relaxed atomics and every thread detects +the same value, so it is on the allowlist as third-party. Then ok: 256 shared writable, 25 +thread-local. The warning that `g_read_only_graph_guard_active` matches nothing is S8's too (the +guard is a debug check). + +## Build and patches + +- `DEPS` changed in 29 commits; synced with `gclient sync --no-history --shallow`. +- `patches/embedpdf-runtime/mac/build.patch` no longer applied: upstream's `build/` now runs the + Apple linker driver through hermetic Python (`95a11c428` in `build/`). Regenerated against the + new `toolchain/apple/toolchain.gni`; same lines. +- `build/` now reads the macOS SDK whenever the host is a Mac (`d9a976071` in `build/`, Sep 9), but + PDFium's `build_overrides/build.gni` sets `use_system_xcode` only for mac and iOS targets, so a + wasm32 build on a Mac stopped in `gn gen`. `build-target.sh` passes `use_system_xcode=true` for + wasm32 on a macOS host (what the darwin builds compute on the same machine). CI builds wasm32 + on Ubuntu and never reads it. +- linuxmusl-arm64 builds without libyuv (`pdf_use_libyuv=false` in `build-target.sh`): the PR's + CI failed in libyuv's `compare_neon64.cc`, whose `udot` (dot product) the musl GCC toolchain's + assembler rejects, even with libyuv's `-march=armv8-a+dotprod+i8mm`; libyuv's SVE2 and SME + files ask for newer extensions still. PDFium keeps its own loops for that setting, which is the + code this target ran before S9. The clang targets keep libyuv. +- The other patches apply (dry-run against the new `build/`): `win/build.patch` (S7's Windows SDK + fix), `wasm/build.patch`, `musl/build.patch`, `musl/pdfium.patch`, `shared-library.patch`, + `skia/geometry-only-stroke.patch`. darwin-arm64 and wasm32 applied and built. + +Build arguments added or changed: +- `build_overrides/pdfium.gni:14`: `pdf_enable_freetype_override = true` +- `pdfium.gni:12`: `pdf_enable_freetype = pdf_enable_freetype_override` +- `pdfium.gni:22`: `pdf_bundle_freetype = pdf_bundle_freetype_override && pdf_enable_freetype` +- `pdfium.gni:47`: `pdf_use_libyuv = true` +- `pdfium.gni:117`: `"pdf_bundle_freetype=true requires pdf_enable_freetype=true")` +- `pdfium.gni:120`: `"pdf_enable_freetype=true is required when building without Skia")` + +We keep the defaults: FreeType on and bundled, libyuv on. + +## API diff + +None. The darwin-arm64 exports are S8's (1150 symbols). + +## Tests + +- [x] fork unit + embedder tests (darwin-arm64): 1270 unit tests pass. Embedder: 1610 tests, + 9 skipped; the failures are S8's 80. Linux runs in the PR's CI. +- [x] layer mode: exit 0; 203 layer-only failures, all expected, as S8. Writers unchanged. +- [x] tag check (`--base 06fbf69b3`): 964 blocks, 0 untagged (one fewer: the popup block went). +- [x] C API check: ok, 521 functions in 22 headers. +- [x] globals check: ok after libyuv's `cpu_info_` went on the allowlist (above). +- [x] wasm build: builds locally (libyuv included), after the `use_system_xcode` fix above. +- [ ] ThreadSanitizer soak: in the PR's CI. +- [x] engine tests and engine-conformance with the local runtime (Oct 13): the same failing tests + as S8's runtime, `@embedpdf/engine` 34 of 1616 and `@cloudpdf/engine` 20 of 937; licensing + passes. `test:redaction-perf` fails 10 of 10, as in every run since the baseline: the engine + commit used for the check still calls `EPDFAnnot_ApplyRedaction`, which the fork replaced (see + S1's report). Generated bindings are S8's (only the clang AST's node addresses differ). +- changed expectations, each with its reason: + - `CPDFAnnotListTest.ClosedPopupAppearanceObjects` (new upstream): building the list leaves the + text annotation without `/AP`; its appearance is drawn from memory (reads-dont-write). + - `FPDFAnnotEmbedderTest.Bug1206`: upstream's minimum-size check replaces our `> 0`; our + saved-copy render check stays. + +## Ledger changes proposed + +For review; none applied. + +1. **substitute-font-bounds, Where:** "`cfx_face.cpp`, inside upstream's `PDF_ENABLE_FREETYPE` + guards: the scoped variation calls FreeType." **When upstream touches this:** add "the FreeType + and Fontations branches of `CFX_Face`". +2. **fonts, When upstream touches this:** add "Our font code calls FreeType directly + (`CFX_Face::GetFsTypeFlags()`, CID-keyed CFF, the registry), so our builds keep + `pdf_enable_freetype` on; a Fontations-only build would need Skrifa versions of them." diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn index d43d16a075..d6059f391c 100644 --- a/fpdfsdk/BUILD.gn +++ b/fpdfsdk/BUILD.gn @@ -125,24 +125,26 @@ source_set("fpdfsdk") { "../:pdfium_strict_config", "../:pdfium_noshorten_config", ] - deps = [ - "//third_party:png", + public_deps = [ "../:pdfium_public_headers", + "../core/fpdfapi/page", + "../core/fpdfapi/parser", + "../core/fpdfdoc", + "../core/fxcrt", + "../core/fxge", + "formfiller", + "pwl", + ] + deps = [ "../constants", "../core/fdrm", "../core/fpdfapi/edit", "../core/fpdfapi/font", - "../core/fpdfapi/page", - "../core/fpdfapi/parser", "../core/fpdfapi/render", - "../core/fpdfdoc", "../core/fpdftext", "../core/fxcodec", - "../core/fxcrt", - "../core/fxge", "../fxjs", - "formfiller", - "pwl", + "//third_party:png", ] allow_circular_includes_from = [ "../fxjs", @@ -161,6 +163,29 @@ source_set("fpdfsdk") { } } +source_set("test_support") { + testonly = true + sources = [ + "display_list_renderer.cpp", + "display_list_renderer.h", + ] + configs += [ + "../:pdfium_strict_config", + "../:pdfium_noshorten_config", + ] + deps = [ + ":fpdfsdk", + "../:pdfium_public_headers", + "../core/fpdfapi/page", + "../core/fpdfapi/parser", + "../core/fpdfapi/render", + "../core/fxcrt", + "../core/fxge", + "../core/fxge:test_support", + ] + visibility = [ "../*" ] +} + pdfium_unittest_source_set("unittests") { sources = [ "cpdfsdk_helpers_unittest.cpp", @@ -178,6 +203,7 @@ pdfium_unittest_source_set("unittests") { "../core/fpdfapi/parser:unit_test_support", "../core/fpdfapi/render", "../core/fpdfdoc", + "../testing:test_support", ] pdfium_root_dir = "../" } @@ -281,6 +307,8 @@ pdfium_embeddertest_source_set("embeddertests") { "../core/fpdfapi/parser", "../core/fxcrt:test_support", "../core/fxge", + "../testing:path_service", + "../testing:test_support", ] pdfium_root_dir = "../" diff --git a/fpdfsdk/DEPS b/fpdfsdk/DEPS index 7c77a7e523..ba65b0ce92 100644 --- a/fpdfsdk/DEPS +++ b/fpdfsdk/DEPS @@ -9,7 +9,7 @@ include_rules = [ ] specific_include_rules = { - 'fpdf_view_embeddertest\.cpp': [ + 'fpdf_view_embeddertest\\.cpp': [ '+third_party/skia/include', ], } diff --git a/fpdfsdk/cpdfsdk_appstream.cpp b/fpdfsdk/cpdfsdk_appstream.cpp index 6afe55baea..44f4e0625e 100644 --- a/fpdfsdk/cpdfsdk_appstream.cpp +++ b/fpdfsdk/cpdfsdk_appstream.cpp @@ -633,7 +633,8 @@ ByteString GetWordRenderString(ByteStringView strWords) { if (strWords.IsEmpty()) { return ByteString(); } - return PDF_EncodeString(strWords) + " " + kShowTextOperator + "\n"; + ByteString encoded = PDF_EncodeString(strWords); + return ByteString({encoded.AsStringView(), ' ', kShowTextOperator, '\n'}); } ByteString GetEditAppStream(CPWL_EditImpl* pEdit, @@ -1596,8 +1597,12 @@ void CPDFSDK_AppStream::SetAsComboBox(std::optional sValue) { } sBody << GetDropButtonAppStream(rcButton); + ByteString background = GetBackgroundAppStream(); + ByteString border = GetBorderAppStream(); + ByteString body(sBody); Write("N", - GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody), + ByteString({background.AsStringView(), border.AsStringView(), + body.AsStringView()}), ByteString()); } @@ -1678,8 +1683,12 @@ void CPDFSDK_AppStream::SetAsListBox() { << kEndPathNoFillOrStrokeOperator << "\n" << sList.str(); } + ByteString background = GetBackgroundAppStream(); + ByteString border = GetBorderAppStream(); + ByteString body(sBody); Write("N", - GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sBody), + ByteString({background.AsStringView(), border.AsStringView(), + body.AsStringView()}), ByteString()); } @@ -1824,9 +1833,13 @@ void CPDFSDK_AppStream::SetAsTextField(std::optional sValue) { } } + ByteString background = GetBackgroundAppStream(); + ByteString border = GetBorderAppStream(); + ByteString lines(sLines); + ByteString body(sBody); Write("N", - GetBackgroundAppStream() + GetBorderAppStream() + ByteString(sLines) + - ByteString(sBody), + ByteString({background.AsStringView(), border.AsStringView(), + lines.AsStringView(), body.AsStringView()}), ByteString()); } diff --git a/fpdfsdk/display_list_renderer.cpp b/fpdfsdk/display_list_renderer.cpp new file mode 100644 index 0000000000..3400d24537 --- /dev/null +++ b/fpdfsdk/display_list_renderer.cpp @@ -0,0 +1,98 @@ +// 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 "fpdfsdk/display_list_renderer.h" + +#include + +#include "core/fpdfapi/page/cpdf_occontext.h" +#include "core/fpdfapi/page/cpdf_page.h" +#include "core/fpdfapi/parser/cpdf_document.h" +#include "core/fpdfapi/render/cpdf_pagerendercontext.h" +#include "core/fpdfapi/render/cpdf_renderoptions.h" +#include "core/fxcrt/bytestring.h" +#include "core/fxcrt/fx_coordinates.h" +#include "core/fxcrt/retain_ptr.h" +#include "core/fxge/cfx_renderdevice.h" +#include "core/fxge/display_list_device_driver.h" +#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fpdfsdk/cpdfsdk_helpers.h" +#include "fpdfsdk/cpdfsdk_pageview.h" +#include "fpdfsdk/cpdfsdk_renderpage.h" +#include "public/fpdfview.h" + +// static +std::string DisplayListRenderer::RenderPage(FPDF_PAGE page, + FPDF_FORMHANDLE form, + int flags) { + int width = static_cast(FPDF_GetPageWidthF(page)); + int height = static_cast(FPDF_GetPageHeightF(page)); + DisplayListRenderer renderer(page, width, height, flags); + if (!renderer.Start()) { + return std::string(); + } + renderer.Finish(form); + return renderer.GetDisplayList(); +} + +DisplayListRenderer::DisplayListRenderer(FPDF_PAGE page, + int width, + int height, + int flags) + : page_(page), width_(width), height_(height), flags_(flags) { + auto driver = std::make_unique(width, height); + driver_ = driver.get(); + device_ = CFX_RenderDevice::CreateWithDriver(std::move(driver)); +} + +DisplayListRenderer::~DisplayListRenderer() = default; + +bool DisplayListRenderer::Start() { + if (!device_) { + return false; + } + CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page_); + if (!cpdf_page) { + return false; + } + context_ = std::make_unique(); + context_->device_ = std::move(device_); + CPDFSDK_RenderPageWithContext(context_.get(), cpdf_page, /*start_x=*/0, + /*start_y=*/0, /*size_x=*/width_, + /*size_y=*/height_, /*rotate=*/0, + /*flags=*/flags_, nullptr, + /*need_to_restore=*/true, nullptr); + return true; +} + +void DisplayListRenderer::Finish(FPDF_FORMHANDLE form) { + if (form && context_ && context_->device_) { + CPDFSDK_FormFillEnvironment* form_fill_env = + CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form); + IPDF_Page* ipdf_page = IPDFPageFromFPDFPage(page_); + if (form_fill_env && ipdf_page) { + CPDFSDK_PageView* page_view = + form_fill_env->GetOrCreatePageView(ipdf_page); + if (page_view) { + CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page_); + const FX_RECT rect(0, 0, width_, height_); + CFX_Matrix matrix = cpdf_page->GetDisplayMatrixForRect(rect, 0); + CPDF_RenderOptions options; + options.GetOptions().bClearType = !!(flags_ & FPDF_LCD_TEXT); + if (flags_ & FPDF_GRAYSCALE) { + options.SetColorMode(CPDF_RenderOptions::kGray); + } + options.SetDrawAnnots(flags_ & FPDF_ANNOT); + options.SetOCContext(pdfium::MakeRetain( + cpdf_page->GetDocument(), CPDF_OCContext::kView)); + page_view->PageView_OnDraw(context_->device_.get(), matrix, &options, + rect); + } + } + } +} + +std::string DisplayListRenderer::GetDisplayList() const { + return std::string(driver_->display_list().c_str()); +} diff --git a/fpdfsdk/display_list_renderer.h b/fpdfsdk/display_list_renderer.h new file mode 100644 index 0000000000..e597d00900 --- /dev/null +++ b/fpdfsdk/display_list_renderer.h @@ -0,0 +1,43 @@ +// 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 FPDFSDK_DISPLAY_LIST_RENDERER_H_ +#define FPDFSDK_DISPLAY_LIST_RENDERER_H_ + +#include +#include + +#include "core/fxcrt/unowned_ptr.h" +#include "public/fpdfview.h" + +class CFX_RenderDevice; +class CPDF_PageRenderContext; +class DisplayListDeviceDriver; + +class DisplayListRenderer { + public: + // Convenience method for one-shot synchronous rendering of a page. + static std::string RenderPage(FPDF_PAGE page, + FPDF_FORMHANDLE form, + int flags); + + DisplayListRenderer(FPDF_PAGE page, int width, int height, int flags); + ~DisplayListRenderer(); + + bool Start(); + void Finish(FPDF_FORMHANDLE form); + + std::string GetDisplayList() const; + + private: + FPDF_PAGE const page_; + const int width_; + const int height_; + const int flags_; + std::unique_ptr context_; // must outlive `driver_` + std::unique_ptr device_; // must outlive `driver_` + UnownedPtr driver_; +}; + +#endif // FPDFSDK_DISPLAY_LIST_RENDERER_H_ diff --git a/fpdfsdk/formfiller/BUILD.gn b/fpdfsdk/formfiller/BUILD.gn index 857bb5a698..4652b0f408 100644 --- a/fpdfsdk/formfiller/BUILD.gn +++ b/fpdfsdk/formfiller/BUILD.gn @@ -36,15 +36,17 @@ source_set("formfiller") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - deps = [ + public_deps = [ "../../:pdfium_public_headers", - "../../constants", - "../../core/fpdfapi/page", "../../core/fpdfdoc", "../../core/fxcrt", - "../../core/fxge", "../pwl", ] + deps = [ + "../../constants", + "../../core/fpdfapi/page", + "../../core/fxge", + ] visibility = [ "../../*" ] } diff --git a/fpdfsdk/formfiller/cffl_formfield.cpp b/fpdfsdk/formfiller/cffl_formfield.cpp index 947fa14a51..790ad3e878 100644 --- a/fpdfsdk/formfiller/cffl_formfield.cpp +++ b/fpdfsdk/formfiller/cffl_formfield.cpp @@ -12,6 +12,7 @@ #include "core/fpdfapi/page/cpdf_page.h" #include "core/fxcrt/cfx_bidi_resolver.h" #include "core/fxcrt/check.h" +#include "core/fxcrt/observed_ptr.h" #include "core/fxge/cfx_renderdevice.h" #include "fpdfsdk/cpdfsdk_pageview.h" #include "fpdfsdk/cpdfsdk_widget.h" @@ -289,7 +290,12 @@ void CFFL_FormField::SetFocusForAnnot(CPDFSDK_Widget* pWidget, Mask nFlag) { CPDFSDK_PageView* pPageView = form_filler_->GetOrCreatePageView(pWidget->GetPage()); + + ObservedPtr observed_this(this); CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView); + if (!observed_this) { + return; + } if (pWnd) { pWnd->SetFocus(); } @@ -304,7 +310,15 @@ void CFFL_FormField::KillFocusForAnnot(Mask nFlag) { } CPDFSDK_PageView* pPageView = form_filler_->GetPageView(widget_->GetPage()); - if (!pPageView || !CommitData(pPageView, nFlag)) { + if (!pPageView) { + return; + } + + ObservedPtr observed_this(this); + if (!CommitData(pPageView, nFlag)) { + return; + } + if (!observed_this) { return; } if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView)) { @@ -510,41 +524,40 @@ bool CFFL_FormField::CommitData(const CPDFSDK_PageView* pPageView, return true; } - ObservedPtr pObserved(widget_); - if (!form_filler_->OnKeyStrokeCommit(pObserved, pPageView, nFlag)) { - if (!pObserved) { - return false; - } - ResetPWLWindow(pPageView); - return true; - } - if (!pObserved) { + ObservedPtr observed_this(this); + ObservedPtr observed_widget(widget_); + const bool keystroke_committed = + form_filler_->OnKeyStrokeCommit(observed_widget, pPageView, nFlag); + if (!observed_widget || !observed_this) { return false; } - - if (!form_filler_->OnValidate(pObserved, pPageView, nFlag)) { - if (!pObserved) { - return false; - } + if (!keystroke_committed) { ResetPWLWindow(pPageView); - return true; + return observed_this.HasObservable(); } - if (!pObserved) { + + const bool validated = + form_filler_->OnValidate(observed_widget, pPageView, nFlag); + if (!observed_widget || !observed_this) { return false; } + if (!validated) { + ResetPWLWindow(pPageView); + return observed_this.HasObservable(); + } - SaveData(pPageView); // may invoking JS to delete this widget. - if (!pObserved) { + SaveData(pPageView); // May invoke JS to delete this widget. + if (!observed_widget || !observed_this) { return false; } - form_filler_->OnCalculate(pObserved); - if (!pObserved) { + form_filler_->OnCalculate(observed_widget); + if (!observed_widget || !observed_this) { return false; } - form_filler_->OnFormat(pObserved); - if (!pObserved) { + form_filler_->OnFormat(observed_widget); + if (!observed_widget || !observed_this) { return false; } diff --git a/fpdfsdk/formfiller/cffl_textobject.cpp b/fpdfsdk/formfiller/cffl_textobject.cpp index da6cd80a34..d8743adef2 100644 --- a/fpdfsdk/formfiller/cffl_textobject.cpp +++ b/fpdfsdk/formfiller/cffl_textobject.cpp @@ -8,6 +8,7 @@ #include "core/fpdfapi/page/cpdf_page.h" #include "core/fpdfdoc/cpdf_bafontmap.h" +#include "core/fxcrt/observed_ptr.h" #include "fpdfsdk/cpdfsdk_widget.h" CFFL_TextObject::CFFL_TextObject(CFFL_InteractiveFormFiller* pFormFiller, @@ -22,18 +23,28 @@ CFFL_TextObject::~CFFL_TextObject() { CPWL_Wnd* CFFL_TextObject::ResetPWLWindow(const CPDFSDK_PageView* pPageView) { DestroyPWLWindow(pPageView); - ObservedPtr pRet(CreateOrUpdatePWLWindow(pPageView)); - widget_->UpdateField(); // May invoke JS, invalidating |pRet|. - return pRet.Get(); + ObservedPtr observed_this(this); + ObservedPtr observed_wnd(CreateOrUpdatePWLWindow(pPageView)); + if (!observed_this) { + return nullptr; + } + // May invoke JS, invalidating `observed_wnd` and `this`. + widget_->UpdateField(); + return observed_this ? observed_wnd.Get() : nullptr; } CPWL_Wnd* CFFL_TextObject::RestorePWLWindow(const CPDFSDK_PageView* pPageView) { SavePWLWindowState(pPageView); DestroyPWLWindow(pPageView); RecreatePWLWindowFromSavedState(pPageView); - ObservedPtr pRet(GetPWLWindow(pPageView)); - widget_->UpdateField(); // May invoke JS, invalidating |pRet|. - return pRet.Get(); + ObservedPtr observed_this(this); + ObservedPtr observed_wnd(GetPWLWindow(pPageView)); + if (!observed_this) { + return nullptr; + } + // May invoke JS, invalidating `observed_wnd` and `this`. + widget_->UpdateField(); + return observed_this ? observed_wnd.Get() : nullptr; } CPDF_BAFontMap* CFFL_TextObject::GetOrCreateFontMap() { diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp index d4f02f8500..426b54b8ed 100644 --- a/fpdfsdk/fpdf_annot_embeddertest.cpp +++ b/fpdfsdk/fpdf_annot_embeddertest.cpp @@ -2008,6 +2008,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) { } TEST_F(FPDFAnnotEmbedderTest, Bug1206) { + static constexpr size_t kExpectedMinimumOriginalSize = 1000; + ASSERT_TRUE(OpenDocument("bug_1206.pdf")); ScopedPage page = LoadScopedPage(0); @@ -2015,7 +2017,7 @@ TEST_F(FPDFAnnotEmbedderTest, Bug1206) { ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); const size_t original_size = GetString().size(); - ASSERT_GT(original_size, 0u); + EXPECT_LE(kExpectedMinimumOriginalSize, original_size); // Sanity check. ClearString(); for (size_t i = 0; i < 10; ++i) { diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp index d64347c592..093c48cd8c 100644 --- a/fpdfsdk/fpdf_flatten.cpp +++ b/fpdfsdk/fpdf_flatten.cpp @@ -208,7 +208,7 @@ CFX_FloatRect CalculateRect(std::vector* pRectArray) { } ByteString GenerateFlattenedContent(const ByteString& key) { - return "q 1 0 0 1 0 0 cm /" + key + " Do Q"; + return ByteString({"q 1 0 0 1 0 0 cm /", key.AsStringView(), " Do Q"}); } RetainPtr NewIndirectContentsStreamReference( @@ -242,12 +242,11 @@ void SetPageContents(const ByteString& key, NewIndirectContentsStreamReference(document, "q")); pContentsArray->Append(NewIndirectContentsStreamReference(document, "Q")); } else { - ByteString sStream = "q\n"; + ByteString sStream; { auto pAcc = pdfium::MakeRetain(pContentsStream); pAcc->LoadAllDataFiltered(); - sStream += ByteStringView(pAcc->GetSpan()); - sStream += "\nQ"; + sStream = ByteString({"q\n", ByteStringView(pAcc->GetSpan()), "\nQ"}); } pContentsStream->SetDataAndRemoveFilter(sStream.unsigned_span()); pContentsArray = document->NewIndirect(); @@ -660,8 +659,8 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { fxcrt::ostringstream buf; WriteMatrix(buf, m); ByteString str(buf); - sStream += ByteString::Format("q %s cm /%s Do Q\n", str.c_str(), - sFormName.c_str()); + sStream += ByteString({"q ", str.AsStringView(), " cm /", + sFormName.AsStringView(), " Do Q\n"}); pNewXObject->SetDataAndRemoveFilter(sStream.unsigned_span()); } RemoveFlattenedFields(document, pPageDict.Get()); diff --git a/fpdfsdk/fpdf_formfill.cpp b/fpdfsdk/fpdf_formfill.cpp index 5e01cde1a7..6fc2aac8b2 100644 --- a/fpdfsdk/fpdf_formfill.cpp +++ b/fpdfsdk/fpdf_formfill.cpp @@ -253,8 +253,9 @@ void FFLCommon(FPDF_FORMHANDLE hHandle, std::unique_ptr pDevice; if (dest_is_bitmap) { - pDevice = CFX_RenderDevice::CreateForBitmap( - holder, !!(flags & FPDF_REVERSE_BYTE_ORDER)); + pDevice = + CFX_RenderDevice::CreateForBitmap(holder, /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); } else { #if defined(PDF_USE_SKIA) pDevice = CFX_RenderDevice::CreateForSkiaCanvas(*canvas); diff --git a/fpdfsdk/fpdf_progressive.cpp b/fpdfsdk/fpdf_progressive.cpp index a0555b59fb..0b59c8299f 100644 --- a/fpdfsdk/fpdf_progressive.cpp +++ b/fpdfsdk/fpdf_progressive.cpp @@ -96,8 +96,9 @@ FPDF_RenderPageBitmapWithColorScheme_Start(FPDF_BITMAP bitmap, } #endif - auto device = CFX_RenderDevice::CreateForBitmap( - pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER)); + auto device = + CFX_RenderDevice::CreateForBitmap(pBitmap, /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); if (!device) { #if defined(PDF_USE_SKIA) if (CFX_GEModule::IsSkiaRenderer() && !context->return_premultiplied_) { @@ -234,7 +235,8 @@ EPDF_RenderPageBitmapWithMatrix_Start(FPDF_BITMAP bitmap, #endif auto device = CFX_RenderDevice::CreateForBitmap( - pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER)); + pBitmap, /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); if (!device) { #if defined(PDF_USE_SKIA) if (CFX_GEModule::Get()->UseSkiaRenderer() && diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp index bac53b588e..1dfdb95059 100644 --- a/fpdfsdk/fpdf_transformpage.cpp +++ b/fpdfsdk/fpdf_transformpage.cpp @@ -7,6 +7,7 @@ #include "public/fpdf_transformpage.h" #include +#include #include #include "constants/page_object.h" @@ -75,8 +76,9 @@ void OutputPath(fxcrt::ostringstream& buf, CPDF_Path path) { } pdfium::span points = pPath->GetPoints(); - if (path.IsRect()) { - CFX_PointF diff = points[2].point_ - points[0].point_; + std::optional corner = pPath->GetRectOppositeCorner(); + if (corner.has_value()) { + CFX_PointF diff = corner.value() - points[0].point_; buf << points[0].point_.x << " " << points[0].point_.y << " " << diff.x << " " << diff.y << " re\n"; return; diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp index 0d3ec19a0f..6af8c0c679 100644 --- a/fpdfsdk/fpdf_view.cpp +++ b/fpdfsdk/fpdf_view.cpp @@ -93,6 +93,7 @@ #if BUILDFLAG(IS_WIN) #include "core/fpdfapi/render/cpdf_progressiverenderer.h" +#include "core/fxge/win32/cgdi_device_driver.h" #include "public/fpdf_edit.h" #if defined(PDF_USE_SKIA) @@ -239,24 +240,31 @@ FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* config) { #endif } +#if defined(PDF_ENABLE_FONTATIONS) && defined(PDF_ENABLE_FREETYPE) CFX_FontMgr::FontBackend backend = CFX_FontMgr::FontBackend::kFreeType; if (config && config->version >= 5) { -#if defined(PDF_ENABLE_FONTATIONS) CHECK(config->m_FontLibraryType == FPDF_FONTBACKENDTYPE_FREETYPE || config->m_FontLibraryType == FPDF_FONTBACKENDTYPE_FONTATIONS); if (config->m_FontLibraryType == FPDF_FONTBACKENDTYPE_FONTATIONS) { backend = CFX_FontMgr::FontBackend::kFontations; } -#else - // Builds without Fontations should always use - // `FPDF_FONTBACKENDTYPE_FREETYPE`. + } +#elif defined(PDF_ENABLE_FONTATIONS) + CFX_FontMgr::FontBackend backend = CFX_FontMgr::FontBackend::kFontations; + if (config && config->version >= 5) { + CHECK_EQ(config->m_FontLibraryType, FPDF_FONTBACKENDTYPE_FONTATIONS); + } +#elif defined(PDF_ENABLE_FREETYPE) + CFX_FontMgr::FontBackend backend = CFX_FontMgr::FontBackend::kFreeType; + if (config && config->version >= 5) { CHECK_EQ(config->m_FontLibraryType, FPDF_FONTBACKENDTYPE_FREETYPE); -#endif } +#else +#error "Neither FreeType nor Fontations enabled" +#endif #if defined(PDF_ENABLE_BROTLI) - if (config && config->version >= 6) { - BrotliDecoder::SetBrotliEnabled(config->m_BrotliEnabled); - } + BrotliDecoder::SetBrotliEnabled(config && config->version >= 6 && + config->m_BrotliEnabled); #endif std::optional> user_font_paths_opt; @@ -1153,8 +1161,8 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_RenderPage(HDC dc, const bool bHasMask = pPage->HasImageMask() && !bNewBitmap; auto* render_data = CPDF_DocRenderData::FromDocument(pPage->GetDocument()); if (!bNewBitmap && !bHasMask) { - context->device_ = CFX_RenderDevice::CreateForWindowsDC( - dc, render_data->GetPSFontTracker()); + context->device_ = CFX_RenderDevice::CreateWithDriver( + CGdiDeviceDriver::CreateDriver(dc, render_data->GetPSFontTracker())); if (!context->device_) { return false; } @@ -1192,8 +1200,8 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_RenderPage(HDC dc, if (!bHasMask) { std::unique_ptr win_dc = - CFX_RenderDevice::CreateForWindowsDC(dc, - render_data->GetPSFontTracker()); + CFX_RenderDevice::CreateWithDriver(CGdiDeviceDriver::CreateDriver( + dc, render_data->GetPSFontTracker())); if (!win_dc) { return false; } @@ -1233,8 +1241,8 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_RenderPage(HDC dc, owned_context = std::make_unique(); context = owned_context.get(); pPage->SetRenderContext(std::move(owned_context)); - context->device_ = - CFX_RenderDevice::CreateForWindowsDC(dc, render_data->GetPSFontTracker()); + context->device_ = CFX_RenderDevice::CreateWithDriver( + CGdiDeviceDriver::CreateDriver(dc, render_data->GetPSFontTracker())); if (!context->device_) { return false; } @@ -1291,7 +1299,8 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap, CFX_DIBitmap::ScopedPremultiplier scoped_premultiplier(pBitmap); #endif auto device = CFX_RenderDevice::CreateForBitmap( - std::move(pBitmap), !!(flags & FPDF_REVERSE_BYTE_ORDER)); + std::move(pBitmap), /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); if (!device) { return; } @@ -1331,7 +1340,8 @@ FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap, CFX_DIBitmap::ScopedPremultiplier scoped_premultiplier(pBitmap); #endif auto device = CFX_RenderDevice::CreateForBitmap( - std::move(pBitmap), !!(flags & FPDF_REVERSE_BYTE_ORDER)); + std::move(pBitmap), /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); if (!device) { return; } @@ -1413,7 +1423,8 @@ EPDF_RenderAnnotBitmap(FPDF_BITMAP bitmap, #endif auto device = CFX_RenderDevice::CreateForBitmap( - std::move(pBitmap), !!(flags & FPDF_REVERSE_BYTE_ORDER)); + std::move(pBitmap), /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); if (!device) { return false; } @@ -1526,7 +1537,8 @@ EPDF_RenderAnnotBitmapUnrotated(FPDF_BITMAP bitmap, #endif auto device = CFX_RenderDevice::CreateForBitmap( - std::move(pBitmap), !!(flags & FPDF_REVERSE_BYTE_ORDER)); + std::move(pBitmap), /*backdrop_bitmap=*/nullptr, + !!(flags & FPDF_REVERSE_BYTE_ORDER)); if (!device) { return false; } @@ -2382,9 +2394,9 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document, ByteStringView bsName; CPDF_DictionaryLocker locker(pDest); for (const auto& it : locker) { - bsName = it.first.AsStringView(); - pDestObj = it.second; if (i == index) { + bsName = it.first.AsStringView(); + pDestObj = it.second->GetDirect(); break; } i++; diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp index ea9ff04064..42bbd3394f 100644 --- a/fpdfsdk/fpdf_view_embeddertest.cpp +++ b/fpdfsdk/fpdf_view_embeddertest.cpp @@ -18,11 +18,13 @@ #include "fpdfsdk/cpdfsdk_helpers.h" #include "fpdfsdk/fpdf_view_c_api_test.h" #include "public/cpp/fpdf_scopers.h" +#include "public/fpdf_doc.h" #include "public/fpdfview.h" #include "testing/embedder_test.h" #include "testing/embedder_test_constants.h" #include "testing/embedder_test_environment.h" #include "testing/fx_string_testhelpers.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/utils/file_util.h" #include "testing/utils/hash.h" @@ -922,6 +924,57 @@ TEST_F(FPDFViewEmbedderTest, NamedDestsOldStyle) { GetPlatformString(reinterpret_cast(buffer))); } +TEST_F(FPDFViewEmbedderTest, NamedDestsOldStyleIndirect) { + ASSERT_TRUE(OpenDocument("named_dests_old_style_indirect.pdf")); + EXPECT_EQ(3u, FPDF_CountNamedDests(document())); + + static constexpr char kIndirectArray[] = "IndirectArray"; + static constexpr char kIndirectDangling[] = "IndirectDangling"; + static constexpr char kIndirectDict[] = "IndirectDict"; + + char buffer[512]; + static constexpr long kBufferSize = sizeof(buffer); + long size = kBufferSize; + + // The first entry is an indirect destination array for the second page. + // Looking it up by index must give the same destination as by name. + FPDF_DEST dest = FPDF_GetNamedDestByName(document(), kIndirectArray); + ASSERT_TRUE(dest); + EXPECT_EQ(1, FPDFDest_GetDestPageIndex(document(), dest)); + EXPECT_EQ(dest, FPDF_GetNamedDest(document(), 0, nullptr, &size)); + ASSERT_EQ(static_cast(sizeof(kIndirectArray) * 2), size); + ASSERT_EQ(dest, FPDF_GetNamedDest(document(), 0, buffer, &size)); + ASSERT_EQ(static_cast(sizeof(kIndirectArray) * 2), size); + EXPECT_EQ(kIndirectArray, + GetPlatformString(reinterpret_cast(buffer))); + + // The second entry is a dangling reference, so neither lookup finds a + // destination, and FPDF_GetNamedDest() does not report the name. + EXPECT_FALSE(FPDF_GetNamedDestByName(document(), kIndirectDangling)); + size = kBufferSize; + EXPECT_FALSE(FPDF_GetNamedDest(document(), 1, nullptr, &size)); + EXPECT_EQ(0, size); + static constexpr char kPattern = 'X'; + std::ranges::fill(buffer, kPattern); + size = kBufferSize; + EXPECT_FALSE(FPDF_GetNamedDest(document(), 1, buffer, &size)); + EXPECT_EQ(kBufferSize, size); // unmodified. + EXPECT_THAT(buffer, testing::Each(kPattern)); + + // The third entry is an indirect destination dictionary, whose /D array is + // for the third page. + dest = FPDF_GetNamedDestByName(document(), kIndirectDict); + ASSERT_TRUE(dest); + EXPECT_EQ(2, FPDFDest_GetDestPageIndex(document(), dest)); + size = kBufferSize; + EXPECT_EQ(dest, FPDF_GetNamedDest(document(), 2, nullptr, &size)); + ASSERT_EQ(static_cast(sizeof(kIndirectDict) * 2), size); + ASSERT_EQ(dest, FPDF_GetNamedDest(document(), 2, buffer, &size)); + ASSERT_EQ(static_cast(sizeof(kIndirectDict) * 2), size); + EXPECT_EQ(kIndirectDict, + GetPlatformString(reinterpret_cast(buffer))); +} + // The following tests pass if the document opens without crashing. TEST_F(FPDFViewEmbedderTest, Crasher113) { ASSERT_TRUE(OpenDocument("bug_113.pdf")); @@ -1994,12 +2047,13 @@ TEST_F(FPDFViewEmbedderTest, RenderXfaPageToSkp) { // Should always be blank, as we're not testing `FPDF_FFLRecord()` here. TestRenderPageSkp(page.get(), pdfium::kBlankPage612By792Png); } +#endif // defined(PDF_USE_SKIA) +#if defined(PDF_USE_AGG) && defined(PDF_USE_SKIA) TEST_F(FPDFViewEmbedderTest, Bug2087) { FPDF_DestroyLibrary(); std::string agg_checksum; -#if defined(PDF_USE_AGG) const FPDF_LIBRARY_CONFIG kAggConfig = { .version = 5, .m_pUserFontPaths = nullptr, @@ -2018,7 +2072,6 @@ TEST_F(FPDFViewEmbedderTest, Bug2087) { } CloseDocument(); FPDF_DestroyLibrary(); -#endif // PDF_USE_AGG std::string skia_checksum; const FPDF_LIBRARY_CONFIG kSkiaConfig = { @@ -2041,7 +2094,7 @@ TEST_F(FPDFViewEmbedderTest, Bug2087) { EmbedderTestEnvironment::GetInstance()->TearDown(); EmbedderTestEnvironment::GetInstance()->SetUp(); } -#endif // defined(PDF_USE_SKIA) +#endif // defined(PDF_USE_AGG) && defined(PDF_USE_SKIA) TEST_F(FPDFViewEmbedderTest, BadIsolatePerDocumentConfig) { FPDF_DestroyLibrary(); @@ -2178,3 +2231,94 @@ TEST_F(FPDFViewEmbedderTest, DocumentVersionInCatalog) { EXPECT_TRUE(FPDF_GetFileVersion(document(), &version)); EXPECT_EQ(16, version); } + +TEST_F(FPDFViewEmbedderTest, RenderPageToDisplayList) { + static const char kExpectedDisplayList[] = + "SaveState\n" + "SetBaseClip [0,0,200,300]\n" + "SetClip_PathFill rule=winding matrix=none " + "points=[move(0.0000,300.0000) line(0.0000,0.0000) line(200.0000,0.0000) " + "line(200.0000,300.0000) line(0.0000,300.0000,close)]\n" + "SaveState\n" + "DrawPath fill=0xff000000 stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(0.0000,290.0000) line(10.0000,290.0000) " + "line(10.0000,300.0000) " + "line(0.0000,300.0000) line(0.0000,290.0000,close)]\n" + "DrawPath fill=0xff000000 stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(10.0000,150.0000) line(60.0000,150.0000) " + "line(60.0000,180.0000) " + "line(10.0000,180.0000) line(10.0000,150.0000,close)]\n" + "DrawPath fill=0xff0000ff stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(190.0000,290.0000) line(200.0000,290.0000) " + "line(200.0000,300.0000) " + "line(190.0000,300.0000) line(190.0000,290.0000,close)]\n" + "DrawPath fill=0xff0000ff stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(70.0000,232.0000) line(120.0000,232.0000) " + "line(120.0000,262.0000) " + "line(70.0000,262.0000) line(70.0000,232.0000,close)]\n" + "DrawPath fill=0xff00ff00 stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(190.0000,0.0000) line(200.0000,0.0000) " + "line(200.0000,10.0000) " + "line(190.0000,10.0000) line(190.0000,0.0000,close)]\n" + "DrawPath fill=0xff00ff00 stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(130.0000,150.0000) line(180.0000,150.0000) " + "line(180.0000,180.0000) " + "line(130.0000,180.0000) line(130.0000,150.0000,close)]\n" + "DrawPath fill=0xffff0000 stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(0.0000,0.0000) line(10.0000,0.0000) line(10.0000,10.0000) " + "line(0.0000,10.0000) line(0.0000,0.0000,close)]\n" + "DrawPath fill=0xffff0000 stroke=0xff000000 rule=even-odd " + "stroke_state=[width=1.0000 cap=butt join=miter miter=10.0000] " + "matrix=[1.0000,0.0000,0.0000,-1.0000,0.0000,300.0000] " + "points=[move(70.0000,67.0000) line(120.0000,67.0000) " + "line(120.0000,97.0000) " + "line(70.0000,97.0000) line(70.0000,67.0000,close)]\n" + "RestoreState\n" + "RestoreState\n"; + + ASSERT_TRUE(OpenDocument("rectangles.pdf")); + ScopedPage page = LoadScopedPage(0); + ASSERT_TRUE(page); + + std::string display_list = RenderPageToDisplayList(page.get()); + EXPECT_EQ(kExpectedDisplayList, display_list); +} + +TEST_F(FPDFViewEmbedderTest, RenderHelloWorldToDisplayList) { + ASSERT_TRUE(OpenDocument("hello_world.pdf")); + ScopedPage page = LoadScopedPage(0); + ASSERT_TRUE(page); + + std::string display_list = RenderPageToDisplayList(page.get()); + EXPECT_NE(std::string::npos, + display_list.find("DrawDeviceText font=\"Tinos\" size=12.0000")); + EXPECT_NE(std::string::npos, + display_list.find("DrawDeviceText font=\"Arimo\" size=16.0000")); + EXPECT_NE(std::string::npos, display_list.find("idx=43,u=U+0048")); +} + +TEST_F(FPDFViewEmbedderTest, RenderImageToDisplayList) { + ASSERT_TRUE(OpenDocument("embedded_images.pdf")); + ScopedPage page = LoadScopedPage(0); + ASSERT_TRUE(page); + + std::string display_list = RenderPageToDisplayList(page.get()); + EXPECT_NE(std::string::npos, + display_list.find("StartDIBits size=[109,88] bpp=24")); + EXPECT_NE(std::string::npos, + display_list.find("StartDIBits size=[92,68] bpp=1")); +} diff --git a/fpdfsdk/fpdfxfa/BUILD.gn b/fpdfsdk/fpdfxfa/BUILD.gn index 1c202f1dc4..9bdba164ba 100644 --- a/fpdfsdk/fpdfxfa/BUILD.gn +++ b/fpdfsdk/fpdfxfa/BUILD.gn @@ -22,22 +22,26 @@ source_set("fpdfxfa") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - deps = [ + public_deps = [ "../../:pdfium_public_headers", - "../../constants", "../../core/fpdfapi/page", "../../core/fpdfapi/parser", - "../../core/fpdfapi/render", - "../../core/fxcodec", "../../core/fxcrt", - "../../fxbarcode", "../../fxjs", "../../fxjs:gc", + "../../xfa/fxfa", + "//v8:cppgc_headers", + ] + deps = [ + "../../constants", + "../../core/fpdfapi/render", + "../../core/fxcodec", + "../../fxbarcode", "../../xfa/fgas/font", "../../xfa/fgas/graphics", "../../xfa/fwl", - "../../xfa/fxfa", "../../xfa/fxfa/parser", + "//v8:v8_headers", ] visibility = [ "../../*" ] } @@ -53,6 +57,8 @@ pdfium_embeddertest_source_set("embeddertests") { ":fpdfxfa", "../:fpdfsdk", "../../fxjs", + "../../testing:path_service", + "../../testing:test_environments", ] pdfium_root_dir = "../../" } diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp index 15c3e4a2a6..062c387dfa 100644 --- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp +++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp @@ -566,9 +566,8 @@ void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc, if (i == pArray->size() - 1) { WideString wPath = WideString::FromUTF16LE(bs.unsigned_span()); ByteString bPath = wPath.ToUTF8(); - static const char kFormat[] = - "\n"; - ByteString content = ByteString::Format(kFormat, bPath.c_str()); + ByteString content({"\n"}); fileWrite->WriteString(content.AsStringView()); } auto pAcc = pdfium::MakeRetain(std::move(pStream)); diff --git a/fpdfsdk/pwl/BUILD.gn b/fpdfsdk/pwl/BUILD.gn index cacff5dc32..c936766768 100644 --- a/fpdfsdk/pwl/BUILD.gn +++ b/fpdfsdk/pwl/BUILD.gn @@ -39,15 +39,17 @@ source_set("pwl") { "../../:pdfium_strict_config", "../../:pdfium_noshorten_config", ] - deps = [ + public_deps = [ "../../:pdfium_public_headers", - "../../constants", - "../../core/fpdfapi/font", - "../../core/fpdfapi/render", "../../core/fpdfdoc", "../../core/fxcrt", "../../core/fxge", ] + deps = [ + "../../constants", + "../../core/fpdfapi/font", + "../../core/fpdfapi/render", + ] visibility = [ "../../*" ] } @@ -58,13 +60,15 @@ source_set("embedder_test_support") { "cpwl_combo_box_embeddertest.h", ] configs += [ "../../:pdfium_strict_config" ] + public_deps = [ + "../../testing:embedder_test_support", + "//testing/gtest", + ] deps = [ ":pwl", "../:fpdfsdk", "../../:pdfium_public_headers", - "../../testing:embedder_test_support", "../formfiller", - "//testing/gtest", ] } diff --git a/fxbarcode/BUILD.gn b/fxbarcode/BUILD.gn index 318cf9d180..78fce1a6d9 100644 --- a/fxbarcode/BUILD.gn +++ b/fxbarcode/BUILD.gn @@ -130,11 +130,11 @@ source_set("fxbarcode") { "qrcode/BC_QRCoderVersion.cpp", "qrcode/BC_QRCoderVersion.h", ] - deps = [ + public_deps = [ "../core/fxcrt", "../core/fxge", - "../third_party:bigint", ] + deps = [ "../third_party:bigint" ] configs += [ "../:pdfium_strict_config" ] visibility = [ "../*" ] } @@ -157,6 +157,7 @@ pdfium_unittest_source_set("unittests") { deps = [ ":fxbarcode", "../core/fxge", + "../testing:test_support", ] pdfium_root_dir = "../" } diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn index 1f6c858b66..849fa58568 100644 --- a/fxjs/BUILD.gn +++ b/fxjs/BUILD.gn @@ -106,13 +106,13 @@ source_set("fxjs") { "../core/fpdfapi/parser", "../core/fpdfapi/render", "../core/fpdfdoc", - "../core/fxge", "//v8", "//v8:v8_libplatform", ] configs += [ "//v8:external_startup_data" ] public_deps += [ "../core/fxcrt", + "../core/fxge", "//v8", ] @@ -208,8 +208,8 @@ source_set("fxjs") { "xfa/fxjse.h", "xfa/jse_define.h", ] + public_deps += [ ":gc" ] deps += [ - ":gc", "../xfa/fgas/crt", "../xfa/fgas/graphics", "../xfa/fxfa/formcalc", @@ -229,7 +229,10 @@ if (pdf_enable_v8) { "gc/heap.h", ] configs += [ "../:pdfium_strict_config" ] - deps = [ "//v8" ] + deps = [ + "//v8", + "//v8:v8_config_headers", + ] public_deps = [ "../core/fxcrt", "//v8:cppgc", @@ -253,6 +256,7 @@ if (pdf_enable_v8) { deps = [ ":fxjs", "../core/fxcrt:unit_test_support", + "//v8:v8_headers", ] pdfium_root_dir = "../" if (pdf_enable_xfa) { @@ -267,7 +271,9 @@ if (pdf_enable_v8) { ] deps += [ ":gc", + "../testing:test_environments", "../xfa/fxfa/parser", + "//v8:cppgc_headers", ] } @@ -285,6 +291,7 @@ if (pdf_enable_v8) { deps = [ ":fxjs", "../fpdfsdk", + "//v8:v8_headers", ] pdfium_root_dir = "../" if (pdf_enable_xfa) { @@ -297,6 +304,8 @@ if (pdf_enable_v8) { ] deps += [ ":gc", + "../testing:test_environments", + "../testing:test_support", "../xfa/fxfa", ] } diff --git a/fxjs/cfxjs_engine.cpp b/fxjs/cfxjs_engine.cpp index 7fa7d49fc7..0aad165ddc 100644 --- a/fxjs/cfxjs_engine.cpp +++ b/fxjs/cfxjs_engine.cpp @@ -187,7 +187,7 @@ class CFXJS_ObjDefinition { fxv8::ThrowExceptionHelper(isolate, "illegal constructor"); return; } - if (info.Data().As()->Value() != FXJSOBJTYPE_DYNAMIC) { + if (info.DataV2().As()->Value() != FXJSOBJTYPE_DYNAMIC) { fxv8::ThrowExceptionHelper(isolate, "not a dynamic object"); return; } diff --git a/fxjs/cjs_delaydata.h b/fxjs/cjs_delaydata.h index 861dbabf3f..cf34d55628 100644 --- a/fxjs/cjs_delaydata.h +++ b/fxjs/cjs_delaydata.h @@ -7,11 +7,14 @@ #ifndef FXJS_CJS_DELAYDATA_H_ #define FXJS_CJS_DELAYDATA_H_ +#include + +#include #include +#include "core/fxcrt/bytestring.h" #include "core/fxcrt/fx_coordinates.h" #include "core/fxcrt/widestring.h" -#include "core/fxge/cfx_color.h" #include "fxjs/cjs_field.h" struct CJS_DelayData { @@ -20,15 +23,14 @@ struct CJS_DelayData { FIELD_PROP eProp; int nControlIndex; - int32_t num = 0; - bool b = false; WideString sFieldName; - ByteString bytestring; - WideString widestring; - CFX_FloatRect rect; - CFX_Color color; - std::vector wordarray; - std::vector widestringarray; + std::variant, + std::vector> + data; }; #endif // FXJS_CJS_DELAYDATA_H_ diff --git a/fxjs/cjs_field.cpp b/fxjs/cjs_field.cpp index 4f0cb5e2ab..40419c2e59 100644 --- a/fxjs/cjs_field.cpp +++ b/fxjs/cjs_field.cpp @@ -2814,28 +2814,28 @@ void CJS_Field::SetDelay(bool bDelay) { void CJS_Field::AddDelay_Int(FIELD_PROP prop, int32_t n) { auto pNewData = std::make_unique(prop, form_control_index_, field_name_); - pNewData->num = n; + pNewData->data = n; js_doc_->AddDelayData(std::move(pNewData)); } void CJS_Field::AddDelay_Bool(FIELD_PROP prop, bool b) { auto pNewData = std::make_unique(prop, form_control_index_, field_name_); - pNewData->b = b; + pNewData->data = b; js_doc_->AddDelayData(std::move(pNewData)); } void CJS_Field::AddDelay_String(FIELD_PROP prop, const ByteString& str) { auto pNewData = std::make_unique(prop, form_control_index_, field_name_); - pNewData->bytestring = str; + pNewData->data = str; js_doc_->AddDelayData(std::move(pNewData)); } void CJS_Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) { auto pNewData = std::make_unique(prop, form_control_index_, field_name_); - pNewData->rect = rect; + pNewData->data = rect; js_doc_->AddDelayData(std::move(pNewData)); } @@ -2843,7 +2843,7 @@ void CJS_Field::AddDelay_WordArray(FIELD_PROP prop, const std::vector& array) { auto pNewData = std::make_unique(prop, form_control_index_, field_name_); - pNewData->wordarray = array; + pNewData->data = array; js_doc_->AddDelayData(std::move(pNewData)); } @@ -2851,7 +2851,7 @@ void CJS_Field::AddDelay_WideStringArray(FIELD_PROP prop, const std::vector& array) { auto pNewData = std::make_unique(prop, form_control_index_, field_name_); - pNewData->widestringarray = array; + pNewData->data = array; js_doc_->AddDelayData(std::move(pNewData)); } @@ -2861,31 +2861,32 @@ void CJS_Field::DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv, switch (pData->eProp) { case FP_BORDERSTYLE: SetBorderStyle(pFormFillEnv, pData->sFieldName, pData->nControlIndex, - pData->bytestring); + std::get(pData->data)); break; case FP_CURRENTVALUEINDICES: SetCurrentValueIndices(pFormFillEnv, pData->sFieldName, - pData->nControlIndex, pData->wordarray); + pData->nControlIndex, + std::get>(pData->data)); break; case FP_DISPLAY: SetDisplay(pFormFillEnv, pData->sFieldName, pData->nControlIndex, - pData->num); + std::get(pData->data)); break; case FP_HIDDEN: SetHidden(pFormFillEnv, pData->sFieldName, pData->nControlIndex, - pData->b); + std::get(pData->data)); break; case FP_LINEWIDTH: SetLineWidth(pFormFillEnv, pData->sFieldName, pData->nControlIndex, - pData->num); + std::get(pData->data)); break; case FP_RECT: SetRect(pFormFillEnv, pData->sFieldName, pData->nControlIndex, - pData->rect); + std::get(pData->data)); break; case FP_VALUE: SetFieldValue(pFormFillEnv, pData->sFieldName, pData->nControlIndex, - pData->widestringarray); + std::get>(pData->data)); break; } } diff --git a/fxjs/js_resources.cpp b/fxjs/js_resources.cpp index 5c054fd876..73de16cbae 100644 --- a/fxjs/js_resources.cpp +++ b/fxjs/js_resources.cpp @@ -97,12 +97,11 @@ WideString JSGetStringFromID(JSMessage msg) { WideString JSFormatErrorString(const char* class_name, const char* property_name, const WideString& details) { - WideString result = WideString::FromUTF8(class_name); + WideString class_str = WideString::FromUTF8(class_name); if (property_name) { - result += L"."; - result += WideString::FromUTF8(property_name); + WideString prop_str = WideString::FromUTF8(property_name); + return WideString({class_str.AsStringView(), L'.', prop_str.AsStringView(), + L": ", details.AsStringView()}); } - result += L": "; - result += details; - return result; + return WideString({class_str.AsStringView(), L": ", details.AsStringView()}); } diff --git a/fxjs/xfa/cfxjse_class.cpp b/fxjs/xfa/cfxjse_class.cpp index 05007e884b..c56c2e9628 100644 --- a/fxjs/xfa/cfxjse_class.cpp +++ b/fxjs/xfa/cfxjse_class.cpp @@ -46,7 +46,7 @@ FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) { void V8FunctionCallback_Wrapper( const v8::FunctionCallbackInfo& info) { const FXJSE_FUNCTION_DESCRIPTOR* pFunctionInfo = - AsFunctionDescriptor(info.Data().As()->Value( + AsFunctionDescriptor(info.DataV2().As()->Value( v8::kExternalPointerTypeTagDefault)); if (!pFunctionInfo) { return; @@ -62,7 +62,7 @@ void V8ConstructorCallback_Wrapper( } const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor = - AsClassDescriptor(info.Data().As()->Value( + AsClassDescriptor(info.DataV2().As()->Value( v8::kExternalPointerTypeTagDefault)); if (!pClassDescriptor) { return; @@ -77,15 +77,14 @@ void V8ConstructorCallback_Wrapper( void Context_GlobalObjToString( const v8::FunctionCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor = - AsClassDescriptor(info.Data().As()->Value( + AsClassDescriptor(info.DataV2().As()->Value( v8::kExternalPointerTypeTagDefault)); if (!pClassDescriptor) { return; } if (pClassDescriptor->name) { - ByteString szStringVal = - ByteString::Format("[object %s]", pClassDescriptor->name); + ByteString szStringVal({"[object ", pClassDescriptor->name, "]"}); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), szStringVal.AsStringView())); return; @@ -99,7 +98,8 @@ void Context_GlobalObjToString( void DynPropGetterAdapter_MethodCallback( const v8::FunctionCallbackInfo& info) { - v8::Local hCallBackInfo = info.Data().As(); + v8::Local hCallBackInfo = + info.DataV2().As().As(); if (hCallBackInfo->InternalFieldCount() != 2) { return; } @@ -211,7 +211,7 @@ v8::Intercepted NamedPropertyQueryCallback( v8::Local property, const v8::PropertyCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClass = - AsClassDescriptor(info.Data().As()->Value( + AsClassDescriptor(info.DataV2().As()->Value( v8::kExternalPointerTypeTagDefault)); if (!pClass) { return v8::Intercepted::kNo; @@ -235,7 +235,7 @@ v8::Intercepted NamedPropertyGetterCallback( v8::Local property, const v8::PropertyCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClass = - AsClassDescriptor(info.Data().As()->Value( + AsClassDescriptor(info.DataV2().As()->Value( v8::kExternalPointerTypeTagDefault)); if (!pClass) { return v8::Intercepted::kNo; @@ -256,7 +256,7 @@ v8::Intercepted NamedPropertySetterCallback( v8::Local value, const v8::PropertyCallbackInfo& info) { const FXJSE_CLASS_DESCRIPTOR* pClass = - AsClassDescriptor(info.Data().As()->Value( + AsClassDescriptor(info.DataV2().As()->Value( v8::kExternalPointerTypeTagDefault)); if (!pClass) { return v8::Intercepted::kNo; diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp index b4ab540a5f..b0684df65c 100644 --- a/fxjs/xfa/cfxjse_formcalc_context.cpp +++ b/fxjs/xfa/cfxjse_formcalc_context.cpp @@ -3579,24 +3579,22 @@ void CFXJSE_FormCalcContext::Format( info.GetReturnValue().SetEmptyString(); return; } - auto wsDatePattern = WideString::FromASCII("date{"); - wsDatePattern += wsPattern.First(iTChar.value()) + L"} "; - - auto wsTimePattern = WideString::FromASCII("time{"); - wsTimePattern += wsPattern.Substr(iTChar.value() + 1) + L"}"; - wsPattern = wsDatePattern + wsTimePattern; + wsPattern = WideString( + {L"date{", wsPattern.AsStringView().First(iTChar.value()), + L"} time{", wsPattern.AsStringView().Substr(iTChar.value() + 1), + L'}'}); } break; case CXFA_LocaleValue::ValueType::kDate: { - wsPattern = L"date{" + wsPattern + L"}"; + wsPattern = WideString({L"date{", wsPattern.AsStringView(), L'}'}); } break; case CXFA_LocaleValue::ValueType::kTime: { - wsPattern = L"time{" + wsPattern + L"}"; + wsPattern = WideString({L"time{", wsPattern.AsStringView(), L'}'}); } break; case CXFA_LocaleValue::ValueType::kText: { - wsPattern = L"text{" + wsPattern + L"}"; + wsPattern = WideString({L"text{", wsPattern.AsStringView(), L'}'}); } break; case CXFA_LocaleValue::ValueType::kFloat: { - wsPattern = L"num{" + wsPattern + L"}"; + wsPattern = WideString({L"num{", wsPattern.AsStringView(), L'}'}); } break; default: { WideString wsTestPattern = L"num{" + wsPattern + L"}"; @@ -3766,11 +3764,9 @@ void CFXJSE_FormCalcContext::Parse( info.GetReturnValue().SetEmptyString(); return; } - WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) + - L"} "); - WideString wsTimePattern(L"time{" + wsPattern.Substr(iTChar.value() + 1) + - L"}"); - wsPattern = wsDatePattern + wsTimePattern; + wsPattern = WideString( + {L"date{", wsPattern.AsStringView().First(iTChar.value()), L"} time{", + wsPattern.AsStringView().Substr(iTChar.value() + 1), L'}'}); CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { @@ -3783,7 +3779,7 @@ void CFXJSE_FormCalcContext::Parse( return; } case CXFA_LocaleValue::ValueType::kDate: { - wsPattern = L"date{" + wsPattern + L"}"; + wsPattern = WideString({L"date{", wsPattern.AsStringView(), L'}'}); CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { @@ -3796,7 +3792,7 @@ void CFXJSE_FormCalcContext::Parse( return; } case CXFA_LocaleValue::ValueType::kTime: { - wsPattern = L"time{" + wsPattern + L"}"; + wsPattern = WideString({L"time{", wsPattern.AsStringView(), L'}'}); CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { @@ -3809,7 +3805,7 @@ void CFXJSE_FormCalcContext::Parse( return; } case CXFA_LocaleValue::ValueType::kText: { - wsPattern = L"text{" + wsPattern + L"}"; + wsPattern = WideString({L"text{", wsPattern.AsStringView(), L'}'}); CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { @@ -3822,7 +3818,7 @@ void CFXJSE_FormCalcContext::Parse( return; } case CXFA_LocaleValue::ValueType::kFloat: { - wsPattern = L"num{" + wsPattern + L"}"; + wsPattern = WideString({L"num{", wsPattern.AsStringView(), L'}'}); CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { @@ -5596,8 +5592,7 @@ void CFXJSE_FormCalcContext::ApplyToObject( void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException( ByteStringView name) const { - ByteString msg(name); - msg += " doesn't have a default property."; + ByteString msg({name, " doesn't have a default property."}); ThrowException(msg.AsStringView()); } @@ -5616,19 +5611,15 @@ void CFXJSE_FormCalcContext::ThrowServerDeniedException() const { void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException( ByteStringView name, ByteStringView exp) const { - ByteString msg("An attempt was made to reference property '"); - msg += name; - msg += "' of a non-object in SOM expression "; - msg += exp; - msg += "."; + ByteString msg({"An attempt was made to reference property '", name, + "' of a non-object in SOM expression ", exp, '.'}); ThrowException(msg.AsStringView()); } void CFXJSE_FormCalcContext::ThrowParamCountMismatchException( ByteStringView method) const { - ByteString msg("Incorrect number of parameters calling method '"); - msg += method; - msg += "'."; + ByteString msg( + {"Incorrect number of parameters calling method '", method, "'."}); ThrowException(msg.AsStringView()); } diff --git a/patches/embedpdf-runtime/mac/build.patch b/patches/embedpdf-runtime/mac/build.patch index d2d70f13ea..22f3986ea8 100644 --- a/patches/embedpdf-runtime/mac/build.patch +++ b/patches/embedpdf-runtime/mac/build.patch @@ -1,8 +1,8 @@ diff --git a/toolchain/apple/toolchain.gni b/toolchain/apple/toolchain.gni -index ec3b67a..c0beb8c 100644 +index d9135a160..8fc71b201 100644 --- a/toolchain/apple/toolchain.gni +++ b/toolchain/apple/toolchain.gni -@@ -502,6 +502,16 @@ template("single_apple_toolchain") { +@@ -632,6 +632,16 @@ template("single_apple_toolchain") { nm = "${prefix}llvm-nm" otool = "${prefix}llvm-otool" @@ -16,6 +16,6 @@ index ec3b67a..c0beb8c 100644 + # headers. + linker_driver_args += " -Wl,-headerpad_max_install_names" + - link_command = "$linker_driver_env $linker_driver" + link_command = "$linker_driver_env \"$python_path\" $linker_driver" link_command += " -Wcrl,otoolpath,$otool -Wcrl,nmpath,$nm" link_command += " -Wcrl,tocname,\"$tocname\"" diff --git a/pdfium.gni b/pdfium.gni index 71cebb8eeb..7d5811d89c 100644 --- a/pdfium.gni +++ b/pdfium.gni @@ -7,6 +7,11 @@ import("//build_overrides/pdfium.gni") # This file contains PDFium-related build flags. +declare_args() { + # Build PDFium with FreeType support. + pdf_enable_freetype = pdf_enable_freetype_override +} + declare_args() { # Build PDFium either: # 1) When set to true, with a bundled FreeType, built from FreeType source @@ -14,7 +19,7 @@ declare_args() { # third_party/freetype/include. # 2) When set to false, use whatever FreeType target is defined in # //build/config/freetype. - pdf_bundle_freetype = pdf_bundle_freetype_override + pdf_bundle_freetype = pdf_bundle_freetype_override && pdf_enable_freetype # Build PDFium with experimental Brotli compression support. pdf_enable_brotli = pdf_enable_brotli_override @@ -38,6 +43,9 @@ declare_args() { # Build PDFium to use AGG (fully supported). pdf_use_agg = true + # Build PDFium to use libyuv for SIMD pixel format conversions. + pdf_use_libyuv = true + # Build PDFium to use Skia (experimental) for all PDFium graphics. # If `pdf_use_agg` is enabled as well, coexists in build with AGG graphics # and the default renderer is selectable at runtime. @@ -109,6 +117,16 @@ assert(!pdf_is_complete_lib || !is_component_build, assert(pdf_use_agg || pdf_use_skia, "At least one of pdf_use_agg or pdf_use_skia must be true") +assert( + pdf_enable_freetype || pdf_enable_fontations, + "At least one of pdf_enable_freetype or pdf_enable_fontations must be true") + +assert(!pdf_bundle_freetype || pdf_enable_freetype, + "pdf_bundle_freetype=true requires pdf_enable_freetype=true") + +assert(pdf_use_skia || pdf_enable_freetype, + "pdf_enable_freetype=true is required when building without Skia") + assert( !pdf_enable_fontations || (enable_rust && enable_rust_cxx), "pdf_enable_fontations=true requires enable_rust=true and enable_rust_cxx=true") diff --git a/public/fpdfview.h b/public/fpdfview.h index 70061ae4c4..10d1f525e2 100644 --- a/public/fpdfview.h +++ b/public/fpdfview.h @@ -26,12 +26,6 @@ #include #endif -#ifdef PDF_ENABLE_XFA -// PDF_USE_XFA is set in confirmation that this version of PDFium can support -// XFA forms as requested by the PDF_ENABLE_XFA setting. -#define PDF_USE_XFA -#endif // PDF_ENABLE_XFA - // PDF object types #define FPDF_OBJECT_UNKNOWN 0 #define FPDF_OBJECT_BOOLEAN 1 diff --git a/scripts/embedpdf-runtime/build-target.sh b/scripts/embedpdf-runtime/build-target.sh index 7ce39b9b17..3a55676881 100755 --- a/scripts/embedpdf-runtime/build-target.sh +++ b/scripts/embedpdf-runtime/build-target.sh @@ -22,6 +22,12 @@ case "$TARGET" in GN_TARGET_OS="emscripten" GN_TARGET_CPU="wasm" EXTRA_ARGS=$'\nis_clang=false\nuse_custom_libcxx=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. + if [[ "$(uname -s)" == "Darwin" ]]; then + EXTRA_ARGS+=$'\nuse_system_xcode=true' + fi ;; darwin-arm64) GN_TARGET_OS="mac" @@ -52,7 +58,10 @@ case "$TARGET" in linuxmusl-arm64) GN_TARGET_OS="linux" GN_TARGET_CPU="arm64" - EXTRA_ARGS=$'\nis_musl=true\nis_clang=false\nuse_sysroot=false\nuse_custom_libcxx=false\nuse_custom_libcxx_for_host=false\nuse_glib=false' + # No libyuv: its arm64 sources use dot-product, SVE2 and SME instructions, + # and the musl GCC toolchain's assembler rejects them. PDFium keeps its own + # loops for pdf_use_libyuv=false. + EXTRA_ARGS=$'\nis_musl=true\nis_clang=false\nuse_sysroot=false\nuse_custom_libcxx=false\nuse_custom_libcxx_for_host=false\nuse_glib=false\npdf_use_libyuv=false' ;; win32-x64) GN_TARGET_OS="win" diff --git a/scripts/embedpdf-runtime/globals-allowlist.txt b/scripts/embedpdf-runtime/globals-allowlist.txt index 202ba46c6f..1104e5f5d4 100644 --- a/scripts/embedpdf-runtime/globals-allowlist.txt +++ b/scripts/embedpdf-runtime/globals-allowlist.txt @@ -78,6 +78,7 @@ third-party | langs | HarfBuzz: the language list, behind atomics. third-party | endchar_str | HarfBuzz CFF: a constant. third-party | only_cleanup_pass_is_decoded | openjpeg: a one-time warning flag, set under a mutex. third-party | Cr_z_arm_cpu_enable_* | zlib: CPU features, detected once. +third-party | cpu_info_ | libyuv: CPU features, detected on first use; read and written with relaxed atomics, and every thread detects the same value. third-party | SkFibonacci47 | Skia path ops: a constant table. third-party | SkMatrix::I()::identity | Skia path ops: a constant. third-party | SkPath::PeekErrorSingleton()::gErrorSingleton | Skia path ops: a once-built singleton. diff --git a/skia/BUILD.gn b/skia/BUILD.gn index a7d439c3a5..b87171aac0 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn @@ -160,7 +160,7 @@ config("skia_config") { "SK_ENABLE_LEGACY_SHADERCONTEXT", ] - if (is_win) { + if (pdf_enable_freetype && is_win) { defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ] } @@ -220,10 +220,10 @@ config("skia_library_config") { "SKIA_IMPLEMENTATION=1", ] - if (use_blink && !use_system_freetype) { + if (pdf_enable_freetype && use_blink && !use_system_freetype) { defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ] } - if (use_blink) { + if (pdf_enable_freetype && use_blink) { defines += [ "SK_TYPEFACE_FACTORY_FREETYPE" ] } if (is_win) { @@ -453,7 +453,7 @@ component("skia") { # FreeType is needed everywhere (except on iOS), on Linux and Android as main # font backend, on Windows and Mac as fallback backend for Variations. - if (!is_ios) { + if (pdf_enable_freetype && !is_ios) { sources += skia_ports_freetype_sources } @@ -476,13 +476,6 @@ component("skia") { ] } - if (is_linux || is_chromeos) { - sources += skia_ports_fci_sources - sources += skia_ports_fontmgr_custom_sources - sources += skia_ports_fontmgr_empty_sources - sources += skia_ports_typeface_proxy_sources - } - if (is_android) { # Unlike Chromium, standalone PDFium on Linux and Chrome OS does not # require these files, since PDFium does not perform Android emulation. @@ -498,11 +491,19 @@ component("skia") { sources += [ "//third_party/skia/src/ports/SkLog_stdio.cpp" ] } - if (is_win || (is_apple && use_blink)) { - # Add the FreeType custom font manager as a fallback backend for variable - # fonts. - sources += skia_ports_fontmgr_custom_sources - sources += skia_ports_fontmgr_empty_sources + if (pdf_enable_freetype) { + if (is_linux || is_chromeos) { + sources += skia_ports_fci_sources + sources += skia_ports_fontmgr_custom_sources + sources += skia_ports_fontmgr_empty_sources + sources += skia_ports_typeface_proxy_sources + } + if (is_win || (is_apple && use_blink)) { + # Add the FreeType custom font manager as a fallback backend for variable + # fonts. + sources += skia_ports_fontmgr_custom_sources + sources += skia_ports_fontmgr_empty_sources + } } if (pdf_enable_fontations) { @@ -535,15 +536,11 @@ component("skia") { ":skcms", ":skia_opts", "../third_party:zlib", - "//:freetype_common", ] public_deps = [ ":skia_core_and_effects" ] if (is_linux || is_chromeos) { - deps += [ - "//third_party:fontconfig", - "//third_party/icu:icuuc", - ] + deps += [ "//third_party/icu:icuuc" ] } if (is_android) { @@ -551,17 +548,23 @@ component("skia") { "//third_party/cpu_features:ndk_compat", "//third_party/expat", ] - } - - if (is_android && !is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] + if (!is_debug) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_max" ] + } } if (is_ios) { frameworks += [ "ImageIO.framework" ] } + if (pdf_enable_freetype) { + deps += [ "//:freetype_common" ] + if (is_linux || is_chromeos) { + deps += [ "//third_party:fontconfig" ] + } + } + if (skia_support_pdf) { sources += skia_pdf_sources } else { @@ -693,6 +696,7 @@ if (current_cpu == "x64") { } visibility = [ ":skcms" ] } + skia_source_set("skcms_TransformSkx") { sources = skcms_TransformSkx if (!is_win) { @@ -711,6 +715,7 @@ if (current_cpu == "x64") { sources = [] visibility = [ ":skcms" ] } + skia_source_set("skcms_TransformSkx") { sources = [] visibility = [ ":skcms" ] diff --git a/testing/BUILD.gn b/testing/BUILD.gn index 3bbb09d823..1ce6782db5 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -44,10 +44,12 @@ source_set("test_support") { data = [ "resources/" ] public_deps = [ ":path_service", + "../:pdfium_public_headers", + "../core/fxcrt", + "../core/fxge:test_support", "//third_party/test_fonts", ] deps = [ - "../:pdfium_public_headers", "../core/fdrm", "../core/fxcrt", "../core/fxge", @@ -57,7 +59,7 @@ source_set("test_support") { configs += [ "../:pdfium_strict_config", "../:pdfium_noshorten_config", - ":pdfium_test_config", + ":pdfium_test_allocator_config", ] visibility = [ "../*" ] if (is_posix) { @@ -81,6 +83,7 @@ source_set("test_support") { deps += [ "//v8", "//v8:v8_libplatform", + "//v8:v8_libplatform_headers", ] configs += [ "//v8:external_startup_data" ] } @@ -135,11 +138,13 @@ source_set("test_environments") { "pdf_test_environment.cpp", "pdf_test_environment.h", ] - deps = [ + public_deps = [ ":test_support", + "//testing/gtest", + ] + deps = [ "../core/fxcrt", "../core/fxge", - "//testing/gtest", ] configs += [ "../:pdfium_strict_config", @@ -150,10 +155,12 @@ source_set("test_environments") { "v8_test_environment.cpp", "v8_test_environment.h", ] + public_deps += [ "../fxjs" ] deps += [ - "../fxjs", "//v8", + "//v8:v8_config_headers", "//v8:v8_libplatform", + "//v8:v8_libplatform_headers", ] configs += [ "//v8:external_startup_data" ] } @@ -172,7 +179,7 @@ source_set("test_environments") { source_set("unit_test_support") { testonly = true sources = [] - deps = [ "//testing/gtest" ] + deps = [] configs += [ "../:pdfium_strict_config", "../:pdfium_noshorten_config", @@ -180,27 +187,32 @@ source_set("unit_test_support") { public_deps = [ ":test_environments", ":test_support", + "//testing/gtest", ] if (is_posix) { sources += [ "temporary_file_test.cpp", "temporary_file_test.h", ] - deps += [ "../core/fxcrt" ] + public_deps += [ "../core/fxcrt" ] } if (pdf_enable_v8) { sources += [ "fxv8_unittest.cpp", "fxv8_unittest.h", ] - deps += [ "../fxjs" ] + deps += [ + "../fxjs", + "//v8:v8_headers", + ] configs += [ "//v8:external_startup_data" ] if (pdf_enable_xfa) { sources += [ "fxgc_unittest.cpp", "fxgc_unittest.h", ] - deps += [ "../fxjs:gc" ] + public_deps += [ "../fxjs:gc" ] + deps += [ "//v8:v8_libplatform_headers" ] } } } @@ -228,22 +240,24 @@ source_set("embedder_test_support") { "utils/compare_coordinates.cc", "utils/compare_coordinates.h", ] - deps = [ + public_deps = [ ":pixel_diff_utils", + ":test_environments", + ":test_support", "../:pdfium_public_headers", + "../core/fxcrt", + "//testing/gmock", + "//testing/gtest", + ] + deps = [ + ":path_service", "../core/fdrm", "../core/fxge", "../fpdfsdk", + "../fpdfsdk:test_support", "png_codec", - "//testing/gmock", - "//testing/gtest", "//third_party/simdutf", ] - public_deps = [ - ":test_environments", - ":test_support", - "../core/fxcrt", - ] configs += [ "../:pdfium_strict_config", "../:pdfium_noshorten_config", @@ -256,6 +270,7 @@ source_set("embedder_test_support") { "js_embedder_test.cpp", "js_embedder_test.h", ] + public_deps += [ "//v8:v8_headers" ] deps += [ "../fxjs", "//v8", @@ -284,12 +299,9 @@ config("pdfium_in_chromium_config") { } } -config("pdfium_test_config") { +config("pdfium_test_allocator_config") { defines = [] include_dirs = [ ".." ] - if (pdf_use_skia) { - defines += [ "PDF_ENABLE_SKIA" ] - } if (is_asan) { defines += [ "PDF_ENABLE_ASAN" ] } diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS index 7574ff3064..b24ec4ecf8 100644 --- a/testing/SUPPRESSIONS +++ b/testing/SUPPRESSIONS @@ -63,6 +63,7 @@ 3_interpolate_image.pdf mac * * skia * fuzzy 3bigpreview.pdf mac * * agg * diff 3bigpreview.pdf * * * * * fuzzy=21,0.15,10.0 +3bigpreview.pdf * * * * fontations fuzzy=25,0.25,15 4_35.pdf mac * * agg * diff 4_35.pdf * * * * fontations fuzzy=10,0.1,15 4_39.pdf mac * * agg * diff @@ -189,7 +190,7 @@ en_contact.pdf * * * * fontations fuzzy=10,0.1,15 en_diy.pdf mac * * agg * diff en_diy.pdf * * * * fontations diff en_foxit.pdf mac * * agg * diff -en_foxit.pdf * * * * fontations fuzzy=10,0.1,15 +en_foxit.pdf * * * * fontations diff en_fqa2.pdf mac * * agg * diff en_fqa2.pdf * * * * fontations fuzzy=10,0.1,15 en_fqa2.pdf mac * * skia * fuzzy @@ -318,6 +319,7 @@ example_033.pdf * * * * fontations diff example_033.pdf * * * * * fuzzy example_034.pdf mac * * agg * diff example_034.pdf * * * * * fuzzy=4,0.05,5.0 +example_034.pdf * * * * fontations fuzzy=10,0.1,15 example_035.pdf mac * * agg * diff example_035.pdf * * * * fontations diff example_035.pdf * * * * * fuzzy @@ -370,6 +372,7 @@ example_050.pdf * * * * fontations diff example_050.pdf * * * * * fuzzy example_051.pdf mac * * agg * diff example_051.pdf * * * * * fuzzy=3,0.35,5.0 +example_051.pdf * * * * fontations fuzzy=10,0.35,15 example_052.pdf mac * * agg * diff example_052.pdf * * * * fontations diff example_052.pdf * * * * * fuzzy @@ -405,6 +408,7 @@ example_061.pdf * * * * fontations diff example_061.pdf * * * * * fuzzy example_062.pdf mac * * agg * diff example_062.pdf * * * * * fuzzy=4,0.05,5.0 +example_062.pdf * * * * fontations fuzzy=10,0.1,15 example_063.pdf mac * * agg * diff example_063.pdf * * * * fontations diff example_063.pdf * * * * * fuzzy @@ -449,7 +453,7 @@ form_combobox_time.pdf * nov8 * * * diff form_list.pdf * * * * * diff form_list1.pdf * * * * * diff form_same.pdf mac * * agg * diff -form_same.pdf * * * * fontations fuzzy=10,0.1,15 +form_same.pdf * * * * fontations diff form_text_sign_url.pdf * * * * * diff format_combo_box.pdf mac * * agg * diff format_combo_box.pdf * * * * fontations fuzzy=10,0.1,15 @@ -568,6 +572,7 @@ time.pdf * * * * fontations fuzzy=10,0.1,15 transformation.pdf mac * * agg * diff transformation.pdf * * * * fontations fuzzy=10,0.1,15 transparent.pdf mac * * agg * diff +transparent.pdf * * * * fontations fuzzy=10,0.1,15 whats_new_in_v3.0.pdf mac * * agg * diff whats_new_in_v3.0.pdf * * * * fontations fuzzy=10,0.1,15 whats_new_in_v3.0.pdf mac * * skia * fuzzy @@ -579,9 +584,9 @@ xfermodes2.pdf * * * agg * diff xfermodes2.pdf * * * skia * fuzzy zh_file1.pdf mac * * agg * diff -zh_file1.pdf * * * * fontations fuzzy=10,0.1,15 +zh_file1.pdf * * * * fontations diff zh_function_list.pdf mac * * agg * diff -zh_function_list.pdf * * * * fontations fuzzy=10,0.1,15 +zh_function_list.pdf * * * * fontations diff zh_function_list.pdf mac * * skia * fuzzy zh_shared_document.pdf mac * * agg * diff zh_shared_document.pdf * * * * fontations diff @@ -613,6 +618,7 @@ Test_CheckBox.pdf * * * * * diff 2_color_tiling.pdf mac_x86 * * skia * diff 2_color_tiling.pdf * * * * fontations fuzzy=10,0.1,15 2_uncolor_tiling.pdf mac_x86 * * skia * diff +2_uncolor_tiling.pdf * * * * fontations fuzzy=10,0.1,15 3bigpreview.pdf mac_x86 * * skia * diff FRC_4.5.3_DeviceRGB_rg.pdf mac_x86 * * skia * diff FRC_4.5.4_CalGray_gamma.pdf mac_x86 * * skia * diff @@ -668,7 +674,7 @@ bug_668762.pdf mac_x86 * * skia * diff bug_668762.pdf * * * * fontations fuzzy=10,0.1,15 edit_transform.pdf mac_x86 * * skia * diff en_14_foxit_products.pdf mac_x86 * * skia * diff -en_14_foxit_products.pdf * * * * fontations fuzzy=10,0.1,15 +en_14_foxit_products.pdf * * * * fontations fuzzy=10,0.2,15 en_contact.pdf mac_x86 * * skia * diff en_foxit.pdf mac_x86 * * skia * diff en_fqa.pdf mac_x86 * * skia * diff @@ -678,9 +684,9 @@ example_004.pdf mac_x86 * * skia * diff example_028.pdf mac_x86 * * skia * diff example_055.pdf mac_x86 * * skia * diff font_1_embedded_font_en_feature.pdf mac_x86 * * skia * diff -font_1_embedded_font_en_feature.pdf * * * * fontations fuzzy=10,0.1,15 +font_1_embedded_font_en_feature.pdf * * * * fontations diff font_2_embedded_font_en_size14.pdf mac_x86 * * skia * diff -font_2_embedded_font_en_size14.pdf * * * * fontations fuzzy=10,0.1,15 +font_2_embedded_font_en_size14.pdf * * * * fontations diff new_stamp4.pdf mac_x86 * * skia * diff octest.pdf mac_x86 * * skia * diff path_5_pattern.pdf mac_x86 * * skia * diff @@ -721,9 +727,11 @@ FRC_8.5_Screen_Img_D_Launch.pdf mac * * skia * fuzzy FRC_8.5_URI_IsMap.pdf * * * gdi * diff FRC_8.5_URI_IsMap.pdf * * * * * fuzzy FRC_8.5_URI_IsMap.pdf mac * * skia * fuzzy +FRC_8.5_URI_IsMap.pdf * * * * fontations fuzzy=10,0.1,15 bug_691967.pdf * * * gdi_skia * diff bug_691967.pdf * * * * * fuzzy bug_691967.pdf mac * * skia * fuzzy +bug_691967.pdf * * * * fontations fuzzy=10,0.1,15 bug_86459.pdf * * * gdi * diff bug_86459.pdf * * * * * fuzzy bug_880920.pdf * * * gdi * diff @@ -822,6 +830,7 @@ whats_new_in_v3.0.pdf * * * gdi * diff # TODO(crbug.com/42271067): Remove after associated bug is fixed Date_FormCale.pdf * * * gdi * diff +Date_FormCale.pdf * * * * fontations fuzzy=10,0.1,15 FRC_3.5_P__3648_Password_1.pdf * * * gdi * diff FRC_3.5_P__3648_Password_1.pdf * * * * fontations fuzzy=10,0.1,15 FRC_8.4.1_Annotations_AP_N_R.D_.pdf * * * gdi * diff @@ -833,7 +842,7 @@ FRC_8.4.1_Annotations_AS_Yes_.pdf * * * * fontations fuzzy=10,0.1,15 FRC_8.5_Bl_Hide.pdf * * * gdi * diff FRC_8.5_Bl_Hide.pdf * * * * fontations diff FRC_8.5_E_GoTo_D.pdf * * * gdi * diff -FRC_8.5_E_GoTo_D.pdf * * * * fontations fuzzy=10,0.1,15 +FRC_8.5_E_GoTo_D.pdf * * * * fontations diff FRC_8.5_Fo_URI_Base.pdf * * * gdi * diff FRC_8.5_Fo_URI_Base.pdf * * * * fontations diff FRC_8.5_U_GoToR_NewWindow.pdf * * * gdi * diff @@ -848,10 +857,13 @@ FRC_8.5_Widget_K.pdf * * * * fontations fuzzy=10,0.1,15 FRC_8.5_Widget_V.pdf * * * gdi * diff FRC_8.5_Widget_V.pdf * * * * fontations fuzzy=10,0.1,15 FRC_8.5_X_GoToR_D.pdf * * * gdi * diff -FRC_8.5_X_GoToR_D.pdf * * * * fontations fuzzy=10,0.1,15 +FRC_8.5_X_GoToR_D.pdf * * * * fontations diff Line_Stroke.pdf * * * gdi * diff +Line_Stroke.pdf * * * * fontations fuzzy=10,0.1,15 Oneof1.pdf * * * gdi * diff +Oneof1.pdf * * * * fontations fuzzy=10,0.1,15 Oneof2.pdf * * * gdi * diff +Oneof2.pdf * * * * fontations fuzzy=10,0.1,15 PagePosition_any_rest.pdf * * * gdi * diff PagePosition_any_rest.pdf * * * * fontations fuzzy=10,0.1,15 PagePosition_first.pdf * * * gdi * diff @@ -890,7 +902,9 @@ Test_RadioButton.pdf * * * * fontations fuzzy=10,0.1,15 Test_ResetButton.pdf * * * gdi * diff Test_ResetButton.pdf * * * * fontations fuzzy=10,0.1,15 Test_TextField.pdf * * * gdi * diff +Test_TextField.pdf * * * * fontations fuzzy=10,0.1,15 TimeField.pdf * * * gdi * diff +TimeField.pdf * * * * fontations fuzzy=10,0.1,15 action_execute_a_menu_item.pdf * * * gdi * diff action_hide_show_form.pdf * * * gdi * diff action_on_focus.pdf * * * gdi * diff @@ -898,7 +912,7 @@ action_reset.pdf * * * gdi * diff action_run_javascript.pdf * * * gdi * diff action_submit_a_form.pdf * * * gdi * diff all_trigger_browsefordoc.pdf * * * gdi * diff -all_trigger_browsefordoc.pdf linux,win * * * fontations diff +all_trigger_browsefordoc.pdf * * * * fontations diff all_trigger_browsefordoc.pdf * * * * * fuzzy all_trigger_browsefordoc.pdf mac * * skia * fuzzy all_trigger_newdoc.pdf * * * gdi * diff @@ -913,6 +927,7 @@ app_launchurl.pdf * * * gdi * diff bi.pdf * * * gdi * diff bi.pdf mac * * skia * fuzzy bug_434.pdf * * * gdi * diff +bug_434.pdf * * * * fontations diff bug_440132.pdf * * * gdi * diff calculate_average.pdf * * * gdi * diff calculate_sum_a_b_c.pdf * * * gdi * diff @@ -1174,11 +1189,13 @@ bug_42270333.in * * * * * diff # TODO(crbug.com/40860237): Remove after associated bug is fixed bug_40860237.in mac * * agg * diff +bug_40860237.in * * * * fontations fuzzy=10,0.1,15 # This test is intentionally suppressed on Windows. Normally, Windows provides # the Symbol font, so this PDF would render correctly. However, in the hermetic # test environment, there is no Symbol font and the rendering is incorrect. bug_1442723.in win * * * * diff +bug_1442723.in * * * * fontations fuzzy=10,0.1,15 # TODO(crbug.com/42270459): Remove after associated bug is fixed bug_42270459_2.in * * * * * diff @@ -1258,8 +1275,10 @@ bug_1383708.in mac * * skia * fuzzy # TODO(crbug.com/42271067): Remove after associated bug is fixed barcode_test.pdf * * * gdi * diff +barcode_test.pdf * * * * fontations fuzzy=10,0.1,15 bug_1026918.in * * * gdi * diff bug_1072440.in * * * gdi * diff +bug_1072440.in * * * * fontations fuzzy=10,0.1,15 bug_113910.in * * * gdi * diff bug_1282.in * * * gdi * diff bug_1286970.in * * * gdi * diff @@ -1284,17 +1303,23 @@ checkbox_radiobutton_hide.in * * * gdi * diff checkbox_radiobutton_reset.in * * * gdi * diff combobox_form.in * * * gdi * diff dynamic_list_box_allow_multiple_selection.pdf * * * gdi * diff +dynamic_list_box_allow_multiple_selection.pdf * * * * fontations fuzzy=10,0.1,15 dynamic_password_field_background_fill.pdf * * * gdi * diff dynamic_password_field_background_fill.pdf mac * * skia * fuzzy +dynamic_password_field_background_fill.pdf * * * * fontations fuzzy=10,0.1,15 dynamic_table_color_and_width.pdf * * * gdi * diff +dynamic_table_color_and_width.pdf * * * * fontations fuzzy=10,0.1,15 password.in * * * gdi * diff resolve_nodes_0.pdf * * * gdi * diff resolve_nodes_0.pdf * * * * * fuzzy +resolve_nodes_0.pdf * * * * fontations fuzzy=10,0.1,15 scrollable_widgets1.in * * * gdi * diff scrollable_widgets2.in * * * gdi * diff scrollable_widgets2.in * * * * fontations fuzzy=10,0.1,15 static_list_box_caption.pdf * * * gdi * diff +static_list_box_caption.pdf * * * * fontations fuzzy=10,0.1,15 static_password_field_rotate.pdf * * * gdi * diff +static_password_field_rotate.pdf * * * * fontations fuzzy=15,0.1,15 text_form_custom_font.in * * * gdi * diff text_form_multiline.in * * * gdi * diff text_form_multiline.in * * * * fontations fuzzy=10,0.1,15 @@ -1412,7 +1437,7 @@ shading_2_9.pdf mac * * skia * fuzzy zh_file_structure.pdf mac * * skia * fuzzy zh_file_structure.pdf * * * * fontations diff zh_page.pdf mac * * skia * fuzzy -zh_page.pdf * * * * fontations fuzzy=10,0.1,15 +zh_page.pdf * * * * fontations fuzzy=20,0.1,15 zh_webdav.pdf mac * * skia * fuzzy zh_webdav.pdf * * * * fontations fuzzy=10,0.1,15 # @@ -1431,6 +1456,7 @@ bug_846.in * * * * fontations diff # # Fontations fuzzy matching, naming no test that appears above. # +050_extra_m.pdf * * * * fontations fuzzy=10,0.1,15 2_8_stamp1.pdf * * * * fontations fuzzy=10,0.1,15 FRC_10_8.2.2_Prev_remove_all.pdf * * * * fontations fuzzy=10,0.1,15 FRC_10_8.2.4__remove_ModDate_value.pdf * * * * fontations fuzzy=10,0.1,15 @@ -1580,9 +1606,16 @@ FRC_9_8.2.2_Prev_remove_value.pdf * * * * fontations fuzzy=10,0.1,15 FRC_9_8.2.4__remove__Description_obj.pdf * * * * fontations fuzzy=10,0.1,15 annotation_highlight_no_content.pdf * * * * fontations fuzzy=10,0.1,15 bug_113910.in * * * * fontations fuzzy=10,0.1,15 +bug_1308.pdf * * * * fontations fuzzy=15,0.1,15 +bug_1308_1.in * * * * fontations fuzzy=10,0.1,15 +bug_1355.in * * * * fontations fuzzy=15,0.1,15 +bug_1388_2.in * * * * fontations fuzzy=10,0.1,15 +bug_1388_3.in * * * * fontations fuzzy=10,0.1,15 +bug_1402.in * * * * fontations fuzzy=10,0.1,15 bug_1752.in * * * * fontations fuzzy=10,0.1,15 bug_1772.in * * * * fontations fuzzy=10,0.1,15 bug_1922.in * * * * fontations fuzzy=10,0.1,15 +bug_42270459.in * * * * fontations fuzzy=10,0.1,15 bug_432208037.in * * * * fontations fuzzy=10,0.1,15 bug_493126_endobj_bug.pdf * * * * fontations fuzzy=10,0.1,15 bug_493126_endobj_bug_weirdWS.pdf * * * * fontations fuzzy=10,0.1,15 @@ -1617,3 +1650,4 @@ generation_numbers1.pdf * * * * fontations fuzzy=10,0.1,15 generation_numbers2.pdf * * * * fontations fuzzy=10,0.1,15 password.in * * * * fontations fuzzy=10,0.1,15 scrollable_widgets1.in * * * * fontations fuzzy=10,0.1,15 +test_m.pdf * * * * fontations fuzzy=10,0.1,15 diff --git a/testing/command_line_helpers.cpp b/testing/command_line_helpers.cpp index eb2e3ec695..937b5ea5f9 100644 --- a/testing/command_line_helpers.cpp +++ b/testing/command_line_helpers.cpp @@ -22,3 +22,11 @@ FPDF_RENDERER_TYPE GetDefaultRendererType() { return FPDF_RENDERERTYPE_AGG; #endif } + +FPDF_FONT_BACKEND_TYPE GetDefaultFontLibraryType() { +#if defined(PDF_ENABLE_FREETYPE) + return FPDF_FONTBACKENDTYPE_FREETYPE; +#else + return FPDF_FONTBACKENDTYPE_FONTATIONS; +#endif +} diff --git a/testing/command_line_helpers.h b/testing/command_line_helpers.h index cc134cbafb..2acd955322 100644 --- a/testing/command_line_helpers.h +++ b/testing/command_line_helpers.h @@ -20,4 +20,9 @@ bool ParseSwitchKeyValue(const std::string& arg, // based upon command line options. FPDF_RENDERER_TYPE GetDefaultRendererType(); +// Identifies the compile-time default font library to use for font rendering. +// Used as part of support to override the font library at runtime based upon +// command line options. +FPDF_FONT_BACKEND_TYPE GetDefaultFontLibraryType(); + #endif // TESTING_COMMAND_LINE_HELPERS_H_ diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp index d1e4eed45e..887d44e197 100644 --- a/testing/embedder_test.cpp +++ b/testing/embedder_test.cpp @@ -26,6 +26,7 @@ #include "core/fxge/cfx_gemodule.h" #include "core/fxge/dib/fx_dib.h" #include "fpdfsdk/cpdfsdk_helpers.h" +#include "fpdfsdk/display_list_renderer.h" #include "public/cpp/fpdf_scopers.h" #include "public/fpdf_dataavail.h" #include "public/fpdf_edit.h" @@ -948,6 +949,41 @@ ScopedFPDFBitmap EmbedderTest::RenderPage(FPDF_PAGE page) { return RenderPageWithFlags(page, nullptr, 0); } +std::string EmbedderTest::RenderLoadedPageToDisplayList(FPDF_PAGE page) { + return RenderLoadedPageWithFlagsToDisplayList(page, 0); +} + +std::string EmbedderTest::RenderLoadedPageWithFlagsToDisplayList(FPDF_PAGE page, + int flags) { + int page_index = GetPageNumberForLoadedPage(page); + CHECK_GE(page_index, 0); + return RenderPageWithFlagsToDisplayList(page, form_handle(), flags); +} + +std::string EmbedderTest::RenderSavedPageToDisplayList(FPDF_PAGE page) { + return RenderSavedPageWithFlagsToDisplayList(page, 0); +} + +std::string EmbedderTest::RenderSavedPageWithFlagsToDisplayList(FPDF_PAGE page, + int flags) { + int page_index = GetPageNumberForSavedPage(page); + CHECK_GE(page_index, 0); + return RenderPageWithFlagsToDisplayList(page, saved_form_handle(), flags); +} + +// static +std::string EmbedderTest::RenderPageWithFlagsToDisplayList( + FPDF_PAGE page, + FPDF_FORMHANDLE handle, + int flags) { + return DisplayListRenderer::RenderPage(page, handle, flags); +} + +// static +std::string EmbedderTest::RenderPageToDisplayList(FPDF_PAGE page) { + return RenderPageWithFlagsToDisplayList(page, nullptr, 0); +} + #if BUILDFLAG(IS_WIN) // static std::vector EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page, diff --git a/testing/embedder_test.h b/testing/embedder_test.h index 368997c27e..8236ede7ad 100644 --- a/testing/embedder_test.h +++ b/testing/embedder_test.h @@ -293,6 +293,32 @@ class EmbedderTest : public ::testing::Test, // Simplified form of RenderPageWithFlags() with no handle and no flags. static ScopedFPDFBitmap RenderPage(FPDF_PAGE page); + // RenderLoadedPageWithFlagsToDisplayList() with no flags. + std::string RenderLoadedPageToDisplayList(FPDF_PAGE page); + + // Convert `page` loaded via LoadPage() into a display list string with the + // specified page rendering `flags`. + std::string RenderLoadedPageWithFlagsToDisplayList(FPDF_PAGE page, int flags); + + // RenderSavedPageWithFlagsToDisplayList() with no flags. + std::string RenderSavedPageToDisplayList(FPDF_PAGE page); + + // Convert `page` loaded via LoadSavedPage() into a display list string with + // the specified page rendering `flags`. + std::string RenderSavedPageWithFlagsToDisplayList(FPDF_PAGE page, int flags); + + // Convert `page` into a display list string with the specified page rendering + // `flags`. The form handle associated with `page` should be passed in via + // `handle`. If `handle` is nullptr, then forms on the page will not be + // rendered. + static std::string RenderPageWithFlagsToDisplayList(FPDF_PAGE page, + FPDF_FORMHANDLE handle, + int flags); + + // Simplified form of RenderPageWithFlagsToDisplayList() with no handle and no + // flags. + static std::string RenderPageToDisplayList(FPDF_PAGE page); + #if BUILDFLAG(IS_WIN) // Convert `page` into EMF with the specified page rendering `flags`. static std::vector RenderPageWithFlagsToEmf(FPDF_PAGE page, diff --git a/testing/embedder_test_environment.cpp b/testing/embedder_test_environment.cpp index dee2cbb88a..271ad4e41c 100644 --- a/testing/embedder_test_environment.cpp +++ b/testing/embedder_test_environment.cpp @@ -24,7 +24,8 @@ EmbedderTestEnvironment* g_environment = nullptr; } // namespace EmbedderTestEnvironment::EmbedderTestEnvironment() - : renderer_type_(GetDefaultRendererType()) { + : renderer_type_(GetDefaultRendererType()), + font_library_type_(GetDefaultFontLibraryType()) { DCHECK(!g_environment); g_environment = this; } @@ -46,22 +47,15 @@ void EmbedderTestEnvironment::SetUp() { #ifdef PDF_ENABLE_V8 .m_pIsolate = V8TestEnvironment::GetInstance()->isolate(), - .m_v8EmbedderSlot = 0, .m_pPlatform = V8TestEnvironment::GetInstance()->platform(), -#else // PDF_ENABLE_V8 - .m_pIsolate = nullptr, - .m_v8EmbedderSlot = 0, - .m_pPlatform = nullptr, #endif // PDF_ENABLE_V8 .m_RendererType = renderer_type_, - .m_FontLibraryType = fontations_ ? FPDF_FONTBACKENDTYPE_FONTATIONS - : FPDF_FONTBACKENDTYPE_FREETYPE, + .m_FontLibraryType = font_library_type_, .m_BrotliEnabled = brotli_enabled_, }; FPDF_InitLibraryWithConfig(&config); - test_fonts_.InstallFontMapper(); // EmbedPDF(fonts): standard fonts come from the test fonts, as upstream's // tests expect. The product draws them with the built-in faces; our tests of @@ -99,9 +93,15 @@ void EmbedderTestEnvironment::AddFlag(const std::string& flag) { return; } #endif // defined(PDF_USE_SKIA) +#if defined(PDF_ENABLE_FREETYPE) + if (flag == "--freetype") { + font_library_type_ = FPDF_FONTBACKENDTYPE_FREETYPE; + return; + } +#endif // defined(PDF_ENABLE_FREETYPE) #if defined(PDF_ENABLE_FONTATIONS) if (flag == "--fontations") { - fontations_ = true; + font_library_type_ = FPDF_FONTBACKENDTYPE_FONTATIONS; return; } #endif // defined(PDF_ENABLE_FONTATIONS) diff --git a/testing/embedder_test_environment.h b/testing/embedder_test_environment.h index 3ac640422e..dca8fe5d74 100644 --- a/testing/embedder_test_environment.h +++ b/testing/embedder_test_environment.h @@ -32,14 +32,16 @@ class EmbedderTestEnvironment : public testing::Environment { void AddFlags(int argc, char** argv); bool write_pngs() const { return write_pngs_; } - bool fontations() const { return fontations_; } + bool fontations() const { + return font_library_type_ == FPDF_FONTBACKENDTYPE_FONTATIONS; + } private: void AddFlag(const std::string& flag); int version_ = 6; FPDF_RENDERER_TYPE renderer_type_; - bool fontations_ = false; + FPDF_FONT_BACKEND_TYPE font_library_type_; bool write_pngs_ = false; bool brotli_enabled_ = false; TestFonts test_fonts_; diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn index d9a89721e1..13c9f934da 100644 --- a/testing/fuzzers/BUILD.gn +++ b/testing/fuzzers/BUILD.gn @@ -100,6 +100,7 @@ source_set("fuzzer_init") { if (pdf_enable_xfa) { assert(pdf_enable_v8) + source_set("fuzzer_xfa_process_state") { testonly = !is_component_build sources = [ @@ -107,9 +108,10 @@ if (pdf_enable_xfa) { "xfa_process_state.h", ] configs += [ ":fuzzer_config" ] + public_deps = [ "../../fxjs:gc" ] deps = [ - "../../fxjs:gc", "//v8", + "//v8:v8_libplatform_headers", ] } } @@ -131,6 +133,7 @@ source_set("fuzzer_init_public") { "../:test_support", "../../fxjs", "//v8", + "//v8:v8_config_headers", "//v8:v8_libplatform", ] if (pdf_enable_xfa) { @@ -163,8 +166,8 @@ source_set("fuzzer_helper") { "pdfium_fuzzer_helper.h", ] configs += [ ":fuzzer_config" ] + public_deps = [ "../../:pdfium_public_headers" ] deps = [ - "../../:pdfium_public_headers", "../../core/fxcrt", "../../fpdfsdk", ] @@ -179,7 +182,7 @@ source_set("fuzzer_utils") { "pdfium_fuzzer_util.cc", "pdfium_fuzzer_util.h", ] - deps = [ "../../:pdfium_public_headers" ] + public_deps = [ "../../:pdfium_public_headers" ] include_dirs = [ "../.." ] } @@ -201,6 +204,7 @@ template("pdfium_fuzzer") { impl_name = target_name + "_impl" template_target_name = target_name + source_set("${target_name}_src") { testonly = true sources = [ "component_fuzzer_template.cc" ] @@ -214,6 +218,7 @@ template("pdfium_fuzzer") { } else { impl_name = target_name + "_src" } + source_set(impl_name) { testonly = true sources = invoker.sources @@ -254,6 +259,7 @@ if (pdf_enable_v8) { "../../fxjs", ] } + pdfium_fuzzer("pdf_fx_date_helpers_fuzzer") { sources = [ "pdf_fx_date_helpers_fuzzer.cc" ] deps = [ @@ -267,13 +273,15 @@ if (pdf_enable_v8) { pdfium_fuzzer("pdf_bidi_fuzzer") { sources = [ "pdf_bidi_fuzzer.cc" ] deps = [ - "../../:freetype_common", "../../core/fxcrt", "../../core/fxge", "../../xfa/fgas/font", "../../xfa/fgas/layout", "//third_party/icu:icuuc", ] + if (pdf_enable_freetype) { + deps += [ "../../:freetype_common" ] + } } pdfium_fuzzer("pdf_cfgas_stringformatter_fuzzer") { @@ -286,6 +294,7 @@ if (pdf_enable_v8) { "../../xfa/fgas/crt", "../../xfa/fxfa", "../../xfa/fxfa/parser", + "//v8:cppgc_headers", ] public_fuzzer = true } @@ -459,6 +468,7 @@ if (is_clang) { ] public_fuzzer = true } + pdfium_fuzzer("pdf_xfa_raw_fuzzer") { sources = [ "pdf_xfa_raw_fuzzer.cc" ] deps = [ @@ -467,6 +477,7 @@ if (is_clang) { ] public_fuzzer = true } + pdfium_fuzzer("pdf_xfa_xdp_fdp_fuzzer") { sources = [ "pdf_xfa_xdp_fdp_fuzzer.cc" ] deps = [ @@ -481,11 +492,13 @@ if (is_clang) { pdfium_fuzzer("pdf_cmap_fuzzer") { sources = [ "pdf_cmap_fuzzer.cc" ] deps = [ - "../../:freetype_common", "../../core/fpdfapi/font", "../../core/fxcrt", "../../core/fxge", ] + if (pdf_enable_freetype) { + deps += [ "../../:freetype_common" ] + } } pdfium_fuzzer("pdf_codec_a85_fuzzer") { diff --git a/testing/image_diff.cpp b/testing/image_diff.cpp index 09092f145c..772828fbf6 100644 --- a/testing/image_diff.cpp +++ b/testing/image_diff.cpp @@ -158,6 +158,8 @@ struct DiffMetrics { uint8_t max_delta = 0; double mse = 0.0; double win_mse = 0.0; + double mean_ssim = 1.0; + double min_window_ssim = 1.0; }; // Computes the number of differing pixels. When `out_metrics` is non-null, @@ -190,20 +192,32 @@ int PixelsDifferent(const Image& baseline, out_metrics->raw_diff_pixels = pixels_different; out_metrics->max_delta = max_delta; if (w > 0 && h > 0) { - out_metrics->mse = - static_cast(total_squared_error) / (3.0 * w * h); - - std::vector baseline_overlap(w * h); - std::vector actual_overlap(w * h); - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - baseline_overlap[y * w + x] = baseline.pixel_at(x, y); - actual_overlap[y * w + x] = actual.pixel_at(x, y); + if (pixels_different == 0) { + out_metrics->mse = 0.0; + out_metrics->win_mse = 0.0; + out_metrics->mean_ssim = 1.0; + out_metrics->min_window_ssim = 1.0; + } else { + out_metrics->mse = + static_cast(total_squared_error) / (3.0 * w * h); + + std::vector baseline_overlap(w * h); + std::vector actual_overlap(w * h); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + baseline_overlap[y * w + x] = baseline.pixel_at(x, y); + actual_overlap[y * w + x] = actual.pixel_at(x, y); + } } + out_metrics->win_mse = CalculateMaxWindowMSE( + baseline_overlap, static_cast(w), actual_overlap, + static_cast(w), w, h, kMaxFuzzyWindowSize); + SSIMMetrics ssim_metrics = + CalculateSSIM(baseline_overlap, static_cast(w), + actual_overlap, static_cast(w), w, h); + out_metrics->mean_ssim = ssim_metrics.mean_ssim; + out_metrics->min_window_ssim = ssim_metrics.min_window_ssim; } - out_metrics->win_mse = CalculateMaxWindowMSE( - baseline_overlap, static_cast(w), actual_overlap, - static_cast(w), w, h, kMaxFuzzyWindowSize); } } @@ -325,10 +339,12 @@ int CompareImages(const std::string& binary_name, printf( "%s: actual_w=%d actual_h=%d expected_w=%d expected_h=%d pixels=%d " - "total=%d max_delta=%u mse=%.6f win_mse=%.6f\n", + "total=%d max_delta=%u mse=%.6f win_mse=%.6f mean_ssim=%.6f " + "min_window_ssim=%.6f\n", base_name.c_str(), actual_image.w(), actual_image.h(), baseline_image.w(), baseline_image.h(), metrics.raw_diff_pixels, - overlap_w * overlap_h, metrics.max_delta, metrics.mse, metrics.win_mse); + overlap_w * overlap_h, metrics.max_delta, metrics.mse, metrics.win_mse, + metrics.mean_ssim, metrics.min_window_ssim); } UNSAFE_TODO(printf("%s: %01.2f%% %s (%d pixels differ)\n", diff_name, percent, diff --git a/testing/pdf_test_environment.cpp b/testing/pdf_test_environment.cpp index 5b4f70a1f1..cf8ff83645 100644 --- a/testing/pdf_test_environment.cpp +++ b/testing/pdf_test_environment.cpp @@ -19,9 +19,11 @@ PDFTestEnvironment::~PDFTestEnvironment() = default; void PDFTestEnvironment::SetUp() { CFX_GEModule::Create(test_fonts_.FontPathsSpan(), CFX_GEModule::RendererType::kDefault, -#if defined(PDF_ENABLE_FONTATIONS) +#if defined(PDF_ENABLE_FONTATIONS) && defined(PDF_ENABLE_FREETYPE) fontations_ ? CFX_FontMgr::FontBackend::kFontations : CFX_FontMgr::FontBackend::kFreeType); +#elif defined(PDF_ENABLE_FONTATIONS) + CFX_FontMgr::FontBackend::kFontations); #else CFX_FontMgr::FontBackend::kFreeType); #endif diff --git a/testing/pdfium_test/BUILD.gn b/testing/pdfium_test/BUILD.gn index 424d28ea90..b5ef49f2e9 100644 --- a/testing/pdfium_test/BUILD.gn +++ b/testing/pdfium_test/BUILD.gn @@ -18,20 +18,28 @@ executable("pdfium_test") { "write.cc", "write.h", ] + public_deps = [ "../../:pdfium_public_headers" ] # Note: One should write programs that depend on ../../:pdfium. Whereas this # test program does not set a good example, and depends on PDFium internals, # as well as test support code, for convenience. deps = [ + "../:path_service", "../:test_support", "../../:pdfium_public_headers", + "../../core/fpdfapi/page", + "../../core/fpdfapi/parser", + "../../core/fpdfapi/render", + "../../core/fxcrt", + "../../core/fxge", "../../fpdfsdk", + "../../fpdfsdk:test_support", "../png_codec", "//build/win:default_exe_manifest", ] configs += [ "../:pdfium_in_chromium_config", - "../:pdfium_test_config", + "../:pdfium_test_allocator_config", "../../:pdfium_common_config", ] @@ -44,8 +52,10 @@ executable("pdfium_test") { if (pdf_enable_v8) { deps += [ + "//v8:v8_config_headers", "//v8:v8_headers", "//v8:v8_libplatform", + "//v8:v8_libplatform_headers", ] include_dirs = [ "//v8" ] configs += [ "//v8:external_startup_data" ] diff --git a/testing/pdfium_test/pdfium_test.cc b/testing/pdfium_test/pdfium_test.cc index c584cd7e9e..ba62536d58 100644 --- a/testing/pdfium_test/pdfium_test.cc +++ b/testing/pdfium_test/pdfium_test.cc @@ -19,14 +19,11 @@ #include #include -#if defined(PDF_ENABLE_SKIA) && !defined(PDF_USE_SKIA) -#define PDF_USE_SKIA -#endif - #include "core/fxcrt/check_op.h" #include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/fx_memcpy_wrappers.h" #include "core/fxcrt/span.h" +#include "fpdfsdk/display_list_renderer.h" #include "public/cpp/fpdf_scopers.h" #include "public/fpdf_annot.h" #include "public/fpdf_attachment.h" @@ -69,7 +66,7 @@ #include "testing/allocator_shim_config.h" #endif -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA #include "third_party/skia/include/core/SkCanvas.h" // nogncheck #include "third_party/skia/include/core/SkColor.h" // nogncheck #include "third_party/skia/include/core/SkDocument.h" // nogncheck @@ -92,7 +89,7 @@ #ifdef BUILD_WITH_CHROMIUM #include "testing/chromium_support/discardable_memory_allocator.h" // nogncheck #endif -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA #ifdef PDF_ENABLE_V8 #include "testing/v8_initializer.h" @@ -128,13 +125,25 @@ namespace { enum class RendererType { kDefault, +#if defined(PDF_USE_AGG) kAgg, +#endif // defined(PDF_USE_AGG) #ifdef _WIN32 kGdi, #endif // _WIN32 -#if defined(PDF_ENABLE_SKIA) +#if defined(PDF_USE_SKIA) kSkia, -#endif // defined(PDF_ENABLE_SKIA) +#endif // defined(PDF_USE_SKIA) +}; + +enum class FontLibraryType { + kDefault, +#if defined(PDF_ENABLE_FREETYPE) + kFreetype, +#endif // defined(PDF_ENABLE_FREETYPE) +#if defined(PDF_ENABLE_FONTATIONS) + kFontations, +#endif // defined(PDF_ENABLE_FONTATIONS) }; enum class OutputFormat { @@ -145,6 +154,7 @@ enum class OutputFormat { kPpm, kPng, kAnnot, + kDisplayList, #ifdef _WIN32 kBmp, kEmf, @@ -152,12 +162,12 @@ enum class OutputFormat { kPs3, kPs3Type42, #endif -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA kSkp, #ifdef _WIN32 kXps, #endif // _WIN32 -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA }; struct Options { @@ -188,9 +198,7 @@ struct Options { bool save_thumbnails_decoded = false; bool save_thumbnails_raw = false; RendererType use_renderer_type = RendererType::kDefault; -#if defined(PDF_ENABLE_FONTATIONS) - bool use_fontations_backend = false; -#endif // defined(PDF_ENABLE_FONTATIONS) + FontLibraryType use_font_library_type = FontLibraryType::kDefault; #ifdef PDF_ENABLE_V8 bool disable_javascript = false; std::string js_flags; // Extra flags to pass to v8 init. @@ -573,27 +581,44 @@ bool ParseCommandLine(const std::vector& args, fprintf(stderr, "Duplicate --use-renderer argument\n"); return false; } +#if defined(PDF_USE_AGG) if (value == "agg") { options->use_renderer_type = RendererType::kAgg; + } +#endif // defined(PDF_USE_AGG) #ifdef _WIN32 - } else if (value == "gdi") { + if (value == "gdi") { options->use_renderer_type = RendererType::kGdi; + } #endif // _WIN32 -#if defined(PDF_ENABLE_SKIA) - } else if (value == "skia") { +#if defined(PDF_USE_SKIA) + if (value == "skia") { options->use_renderer_type = RendererType::kSkia; -#endif // defined(PDF_ENABLE_SKIA) - } else { + } +#endif // defined(PDF_USE_SKIA) + if (options->use_renderer_type == RendererType::kDefault) { fprintf(stderr, "Invalid --use-renderer argument\n"); return false; } -#if defined(PDF_ENABLE_SKIA) +#if defined(PDF_USE_SKIA) } else if (cur_arg == "--render-premultiplied") { options->render_premultiplied_alpha = true; -#endif // defined(PDF_ENABLE_SKIA) +#endif // defined(PDF_USE_SKIA) +#if defined(PDF_ENABLE_FREETYPE) + } else if (cur_arg == "--freetype") { + if (options->use_font_library_type != FontLibraryType::kDefault) { + fprintf(stderr, "Duplicate or conflicting --freetype argument\n"); + return false; + } + options->use_font_library_type = FontLibraryType::kFreetype; +#endif // defined(PDF_ENABLE_FONTATIONS) #if defined(PDF_ENABLE_FONTATIONS) } else if (cur_arg == "--fontations") { - options->use_fontations_backend = true; + if (options->use_font_library_type != FontLibraryType::kDefault) { + fprintf(stderr, "Duplicate or conflicting --fontations argument\n"); + return false; + } + options->use_font_library_type = FontLibraryType::kFontations; #endif // defined(PDF_ENABLE_FONTATIONS) #ifdef PDF_ENABLE_V8 } else if (cur_arg == "--disable-javascript") { @@ -647,7 +672,13 @@ bool ParseCommandLine(const std::vector& args, return false; } options->output_format = OutputFormat::kAnnot; -#ifdef PDF_ENABLE_SKIA + } else if (cur_arg == "--dump") { + if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --dump argument\n"); + return false; + } + options->output_format = OutputFormat::kDisplayList; +#ifdef PDF_USE_SKIA } else if (cur_arg == "--skp") { if (options->output_format != OutputFormat::kNone) { fprintf(stderr, "Duplicate or conflicting --skp argument\n"); @@ -662,7 +693,7 @@ bool ParseCommandLine(const std::vector& args, } options->output_format = OutputFormat::kXps; #endif // _WIN32 -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA } else if (ParseSwitchKeyValue(cur_arg, "--font-dir=", &value)) { if (!options->font_directory.empty()) { fprintf(stderr, "Duplicate --font-dir argument\n"); @@ -801,14 +832,14 @@ bool ParseCommandLine(const std::vector& args, } } -#if defined(PDF_ENABLE_SKIA) +#if defined(PDF_USE_SKIA) if (options->render_premultiplied_alpha && options->use_renderer_type != RendererType::kSkia) { fprintf(stderr, "Cannot use --render_premultiplied with selected renderer\n"); return false; } -#endif // defined(PDF_ENABLE_SKIA) +#endif // defined(PDF_USE_SKIA) return true; } @@ -1323,7 +1354,7 @@ class GdiDisplayPageRenderer : public BitmapPageRenderer { }; #endif // _WIN32 -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA class SkCanvasPageRenderer : public PageRenderer { public: bool Start() override { @@ -1470,7 +1501,36 @@ class SkDocumentPageRenderer final : public SkCanvasPageRenderer { SkCanvas* canvas_ = nullptr; bool has_output_ = false; }; -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA + +class DisplayListPageRenderer final : public PageRenderer { + public: + DisplayListPageRenderer(FPDF_PAGE page, int width, int height, int flags) + : PageRenderer(page, width, height, flags), + renderer_(page, width, height, flags) {} + + bool HasOutput() const override { return has_output_; } + + bool Start() override { return renderer_.Start(); } + + void Finish(FPDF_FORMHANDLE form) override { + renderer_.Finish(form); + has_output_ = true; + } + + bool Write(const std::string& name, int page_index, bool /*md5*/) override { + if (!has_output_) { + return false; + } + std::string filename = + WriteDisplayList(name.c_str(), page_index, renderer_.GetDisplayList()); + return !filename.empty(); + } + + private: + DisplayListRenderer renderer_; + bool has_output_ = false; +}; bool PdfProcessor::ProcessPage(const int page_index) { FPDF_PAGE page = GetPage(page_index); @@ -1533,7 +1593,7 @@ bool PdfProcessor::ProcessPage(const int page_index) { case OutputFormat::kPng: { auto func = WriteStraightAlphaBufferToPng; -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA if (options().render_premultiplied_alpha) { func = WritePremultipliedAlphaBufferToPng; } @@ -1559,7 +1619,7 @@ bool PdfProcessor::ProcessPage(const int page_index) { break; #endif // _WIN32 -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA case OutputFormat::kSkp: renderer = std::make_unique( page, /*width=*/width, /*height=*/height, /*flags=*/flags); @@ -1597,7 +1657,12 @@ bool PdfProcessor::ProcessPage(const int page_index) { break; } #endif // _WIN32 -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA + + case OutputFormat::kDisplayList: + renderer = std::make_unique( + page, /*width=*/width, /*height=*/height, /*flags=*/flags); + break; default: // Other formats won't write the output to a file, but still rasterize. @@ -1856,7 +1921,7 @@ void ShowConfig() { #ifdef PDF_ENABLE_ASAN append_config("ASAN"); #endif -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA append_config("SKIA"); #endif #ifdef _WIN32 @@ -1912,7 +1977,7 @@ constexpr char kUsageString[] = " --save-thumbs-raw - write page thumbnails' raw stream data" ".thumbnail.raw..png\n" -#if defined(PDF_ENABLE_SKIA) +#if defined(PDF_USE_SKIA) #ifdef _WIN32 " --use-renderer - renderer to use, one of [agg | gdi | skia]\n" #else @@ -1926,7 +1991,7 @@ constexpr char kUsageString[] = #else " --use-renderer - renderer to use, one of [agg]\n" #endif // _WIN32 -#endif // defined(PDF_ENABLE_SKIA) +#endif // defined(PDF_USE_SKIA) #if defined(PDF_ENABLE_FONTATIONS) " --fontations - Use fontations back-end library\n" #endif @@ -1968,12 +2033,14 @@ constexpr char kUsageString[] = " --png - write page images ..png\n" " --ppm - write page images ..ppm\n" " --annot - write annotation info ..annot.txt\n" -#ifdef PDF_ENABLE_SKIA + " --dump - write page drawing commands " + "..dump.txt\n" +#ifdef PDF_USE_SKIA " --skp - write page images ..skp\n" #ifdef _WIN32 " --xps - write page images ..xps\n" #endif // _WIN32 -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA " --md5 - write output image paths and their md5 hashes to stdout.\n" " --time= - Seconds since the epoch to set system time.\n" ""; @@ -2025,7 +2092,6 @@ int main(int argc, const char* argv[]) { config.m_pIsolate = nullptr; config.m_v8EmbedderSlot = 0; config.m_pPlatform = nullptr; - config.m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE; #ifdef PDF_ENABLE_BROTLI config.m_BrotliEnabled = options.enable_brotli; #else @@ -2042,31 +2108,41 @@ int main(int argc, const char* argv[]) { case RendererType::kDefault: config.m_RendererType = GetDefaultRendererType(); break; - +#if defined(PDF_USE_AGG) case RendererType::kAgg: config.m_RendererType = FPDF_RENDERERTYPE_AGG; break; - +#endif // defined(PDF_USE_AGG) #ifdef _WIN32 case RendererType::kGdi: // GDI renderer uses `FPDF_RenderPage()`, rather than a renderer type. config.m_RendererType = GetDefaultRendererType(); break; #endif // _WIN32 - -#if defined(PDF_ENABLE_SKIA) +#if defined(PDF_USE_SKIA) case RendererType::kSkia: config.m_RendererType = FPDF_RENDERERTYPE_SKIA; break; -#endif // defined(PDF_ENABLE_SKIA) +#endif // defined(PDF_USE_SKIA) } + switch (options.use_font_library_type) { + case FontLibraryType::kDefault: + config.m_FontLibraryType = GetDefaultFontLibraryType(); + break; +#if defined(PDF_ENABLE_FREETYPE) + case FontLibraryType::kFreetype: + config.m_FontLibraryType = FPDF_FONTBACKENDTYPE_FREETYPE; + break; +#endif // defined(PDF_ENABLE_FREETYPE) #if defined(PDF_ENABLE_FONTATIONS) - if (options.use_fontations_backend) { - config.m_FontLibraryType = FPDF_FONTBACKENDTYPE_FONTATIONS; - } + case FontLibraryType::kFontations: + config.m_FontLibraryType = FPDF_FONTBACKENDTYPE_FONTATIONS; + break; #endif // defined(PDF_ENABLE_FONTATIONS) -#if defined(PDF_ENABLE_SKIA) + } + +#if defined(PDF_USE_SKIA) #if defined(BUILD_WITH_CHROMIUM) // Needed to support Chromium's copy of Skia, which uses a // `DiscardableMemoryAllocator`. @@ -2074,7 +2150,7 @@ int main(int argc, const char* argv[]) { chromium_support::InitializeDiscardableMemoryAllocator(); } #endif // defined(BUILD_WITH_CHROMIUM) -#endif // defined(PDF_ENABLE_SKIA) +#endif // defined(PDF_USE_SKIA) std::function idler = [](FPDF_FORMHANDLE) {}; #ifdef PDF_ENABLE_V8 diff --git a/testing/pdfium_test/write.cc b/testing/pdfium_test/write.cc index 821281e4d3..bfafd7e9e1 100644 --- a/testing/pdfium_test/write.cc +++ b/testing/pdfium_test/write.cc @@ -25,7 +25,7 @@ #include "testing/utils/bitmap_saver.h" #include "testing/utils/file_util.h" -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA #include "third_party/skia/include/core/SkData.h" // nogncheck #include "third_party/skia/include/core/SkImage.h" // nogncheck #include "third_party/skia/include/core/SkPicture.h" // nogncheck @@ -36,7 +36,7 @@ #else #include "third_party/skia/include/encode/SkPngEncoder.h" // nogncheck #endif // PDF_ENABLE_RUST_PNG -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA namespace { @@ -250,7 +250,7 @@ std::string WritePng(const char* pdf_name, UNSAFE_TODO(pdfium::span(static_cast(buffer), static_cast(bitmap_attributes.stride) * bitmap_attributes.height)); -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA std::vector straight_alpha_input; if (bitmap_attributes.has_alpha && buffer_has_premultiplied_alpha) { straight_alpha_input = BitmapSaver::ConvertToStraightAlpha( @@ -490,7 +490,7 @@ std::string WriteStraightAlphaBufferToPng( /*buffer_has_premultiplied_alpha=*/false); } -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA std::string WritePremultipliedAlphaBufferToPng( const char* pdf_name, int num, @@ -618,7 +618,7 @@ void WritePS(FPDF_PAGE page, const char* pdf_name, int num) { } #endif // _WIN32 -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA std::unique_ptr WriteToSkWStream(const std::string& pdf_name, int num, const std::string& extension) { @@ -663,7 +663,7 @@ std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture) { picture.serialize(stream.get(), &procs); return filename; } -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA enum class ThumbnailDecodeType { kBitmap, kRawStream, kDecodedStream }; @@ -930,3 +930,16 @@ void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num) { WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename, "thumbnail"); } + +std::string WriteDisplayList(const char* pdf_name, + int num, + const std::string& display_list) { + std::string filename = GeneratePageOutputFilename(pdf_name, num, "dump.txt"); + if (filename.empty()) { + return std::string(); + } + + WriteBufferToFile(display_list.data(), display_list.size(), filename.c_str(), + "dump"); + return filename; +} diff --git a/testing/pdfium_test/write.h b/testing/pdfium_test/write.h index 4e048d36ee..cd6f272695 100644 --- a/testing/pdfium_test/write.h +++ b/testing/pdfium_test/write.h @@ -10,10 +10,10 @@ #include "public/fpdfview.h" -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA class SkPicture; class SkWStream; -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA struct BitmapAttributes { int width; @@ -33,7 +33,7 @@ std::string WriteStraightAlphaBufferToPng( int num, void* buffer, const BitmapAttributes& bitmap_attributes); -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA std::string WritePremultipliedAlphaBufferToPng( const char* pdf_name, int num, @@ -50,7 +50,7 @@ void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num); void WritePS(FPDF_PAGE page, const char* pdf_name, int num); #endif // _WIN32 -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA std::unique_ptr WriteToSkWStream(const std::string& pdf_name, int num, const std::string& extension); @@ -59,7 +59,11 @@ std::unique_ptr WriteToSkWStream(const std::string& pdf_name, const std::string& extension, std::string& filename); std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture); -#endif // PDF_ENABLE_SKIA +#endif // PDF_USE_SKIA + +std::string WriteDisplayList(const char* pdf_name, + int num, + const std::string& display_list); void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name); void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num); diff --git a/testing/resources/brotli_decodeparms.in b/testing/resources/brotli_decodeparms.in index 292c2ab7c0..bf72f025cd 100644 --- a/testing/resources/brotli_decodeparms.in +++ b/testing/resources/brotli_decodeparms.in @@ -15,15 +15,12 @@ endobj {{object 3 0}} << /Type /Page /Parent 2 0 R + /MediaBox [0 0 200 200] /Contents 4 0 R - /MediaBox [0 0 600 200] /Resources << /ProcSet [/PDF /ImageC] - /ColorSpace << - /C1 [/Pattern] - >> - /Pattern << - /P1 5 0 R + /XObject << + /Im1 5 0 R >> >> >> @@ -34,65 +31,13 @@ endobj >> stream q -0.24 0 0 0.24 0 0 cm -/C1 cs -/P1 scn -888 400 108 60 re -f* -0 0 0 rg -888 460 m -888 400 l -S -/C1 cs -/P1 scn -1536 400 108 60 re -f* -0 0 0 rg -1536 460 m -1536 400 l -S -/C1 cs -/P1 scn -2184 400 108 60 re -f* -0 0 0 rg -2184 460 m -2184 400 l -S +200 0 0 200 0 0 cm +/Im1 Do Q endstream endobj {{object 5 0}} << - /Type /Pattern - /PaintType 1 - /PatternType 1 - /TilingType 1 - /BBox [0 0 128 32] - /Matrix [.202488 0 0 -.449947 0 791.999] - /Resources << - /ProcSet [/PDF /ImageC] - /XObject << - /R17 6 0 R - >> - >> - /XStep 128 - /YStep 32 - {{streamlen}} ->> -stream -0.24 0 0 0.24 0 0 cm -q -0 134 534 -134 re -W n -q 533.333 0 0 -133.333 0 133.333 cm -/R17 Do -Q -Q -endstream -endobj - -{{object 6 0}} << /Type /XObject /Subtype /Image /Width 4 diff --git a/testing/resources/brotli_decodeparms.pdf b/testing/resources/brotli_decodeparms.pdf index 23c805d99e..c755be06db 100644 Binary files a/testing/resources/brotli_decodeparms.pdf and b/testing/resources/brotli_decodeparms.pdf differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_base_content_skia_linux.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_base_content_skia_linux.png index 22d3f3ca68..fde8d83089 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_base_content_skia_linux.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_base_content_skia_linux.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_image_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_image_skia.png index d86556b54b..d94e1f78a1 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_image_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_image_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_path_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_path_skia.png index 877616c539..1b2ab8804d 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_path_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_path_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_text_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_text_skia.png index c138198bca..59a97cc0cb 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_text_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_modified_text_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_new_annot_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_new_annot_skia.png index cba0a70540..7ded6a3846 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_new_annot_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_new_annot_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_new_image_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_new_image_skia.png index a2976e659a..779c3d2b5f 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_new_image_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_new_image_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_new_text_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_new_text_skia.png index 83f0049b68..242e9a8e2e 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_new_text_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_new_text_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_skia.png index f3dc7d3245..1e7a97a345 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_skia.png differ diff --git a/testing/resources/embedder_tests/annotation_stamp_with_ap_two_paths_skia.png b/testing/resources/embedder_tests/annotation_stamp_with_ap_two_paths_skia.png index 7872b4427c..7772c1a152 100644 Binary files a/testing/resources/embedder_tests/annotation_stamp_with_ap_two_paths_skia.png and b/testing/resources/embedder_tests/annotation_stamp_with_ap_two_paths_skia.png differ diff --git a/testing/resources/embedder_tests/annots_page1_focus_highlight_skia_linux.png b/testing/resources/embedder_tests/annots_page1_focus_highlight_skia_linux.png index e595c2bcd2..8d587ab07c 100644 Binary files a/testing/resources/embedder_tests/annots_page1_focus_highlight_skia_linux.png and b/testing/resources/embedder_tests/annots_page1_focus_highlight_skia_linux.png differ diff --git a/testing/resources/embedder_tests/annots_page1_focus_link_skia_linux.png b/testing/resources/embedder_tests/annots_page1_focus_link_skia_linux.png index 2706954d24..bc18976b2b 100644 Binary files a/testing/resources/embedder_tests/annots_page1_focus_link_skia_linux.png and b/testing/resources/embedder_tests/annots_page1_focus_link_skia_linux.png differ diff --git a/testing/resources/embedder_tests/annots_page1_skia_linux.png b/testing/resources/embedder_tests/annots_page1_skia_linux.png index 81a93127d7..7df1cd9f1b 100644 Binary files a/testing/resources/embedder_tests/annots_page1_skia_linux.png and b/testing/resources/embedder_tests/annots_page1_skia_linux.png differ diff --git a/testing/resources/embedder_tests/annots_page2_modified_skia_linux.png b/testing/resources/embedder_tests/annots_page2_modified_skia_linux.png index 2c13a5b5af..f24d344a22 100644 Binary files a/testing/resources/embedder_tests/annots_page2_modified_skia_linux.png and b/testing/resources/embedder_tests/annots_page2_modified_skia_linux.png differ diff --git a/testing/resources/embedder_tests/annots_page2_skia_linux.png b/testing/resources/embedder_tests/annots_page2_skia_linux.png index e25b9e0eef..7b2b80ace3 100644 Binary files a/testing/resources/embedder_tests/annots_page2_skia_linux.png and b/testing/resources/embedder_tests/annots_page2_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bottom_text_added_bold_and_stretched_text_skia_linux.png b/testing/resources/embedder_tests/bottom_text_added_bold_and_stretched_text_skia_linux.png index 1dc83c74c7..a696b87a0f 100644 Binary files a/testing/resources/embedder_tests/bottom_text_added_bold_and_stretched_text_skia_linux.png and b/testing/resources/embedder_tests/bottom_text_added_bold_and_stretched_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bottom_text_added_bold_text_skia_linux.png b/testing/resources/embedder_tests/bottom_text_added_bold_text_skia_linux.png index dad708444d..015a80908d 100644 Binary files a/testing/resources/embedder_tests/bottom_text_added_bold_text_skia_linux.png and b/testing/resources/embedder_tests/bottom_text_added_bold_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bottom_text_skia_linux.png b/testing/resources/embedder_tests/bottom_text_skia_linux.png index bc0fd22122..cc9d46c40c 100644 Binary files a/testing/resources/embedder_tests/bottom_text_skia_linux.png and b/testing/resources/embedder_tests/bottom_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bottom_text_text_object_skia_linux.png b/testing/resources/embedder_tests/bottom_text_text_object_skia_linux.png index 3efd060ec5..f90d8344d1 100644 Binary files a/testing/resources/embedder_tests/bottom_text_text_object_skia_linux.png and b/testing/resources/embedder_tests/bottom_text_text_object_skia_linux.png differ diff --git a/testing/resources/embedder_tests/brotli_decode_parms.png b/testing/resources/embedder_tests/brotli_decode_parms.png new file mode 100644 index 0000000000..f3656047b3 Binary files /dev/null and b/testing/resources/embedder_tests/brotli_decode_parms.png differ diff --git a/testing/resources/embedder_tests/brotli_decode_parms_agg.png b/testing/resources/embedder_tests/brotli_decode_parms_agg.png deleted file mode 100644 index c87b9aac62..0000000000 Binary files a/testing/resources/embedder_tests/brotli_decode_parms_agg.png and /dev/null differ diff --git a/testing/resources/embedder_tests/brotli_decode_parms_skia.png b/testing/resources/embedder_tests/brotli_decode_parms_skia.png deleted file mode 100644 index 0a4705ba31..0000000000 Binary files a/testing/resources/embedder_tests/brotli_decode_parms_skia.png and /dev/null differ diff --git a/testing/resources/embedder_tests/bug_1558_skia_linux.png b/testing/resources/embedder_tests/bug_1558_skia_linux.png index 2c2a8c76f5..6143725f07 100644 Binary files a/testing/resources/embedder_tests/bug_1558_skia_linux.png and b/testing/resources/embedder_tests/bug_1558_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bug_1574_skia_linux.png b/testing/resources/embedder_tests/bug_1574_skia_linux.png index 13a610774c..683603c988 100644 Binary files a/testing/resources/embedder_tests/bug_1574_skia_linux.png and b/testing/resources/embedder_tests/bug_1574_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bug_1893_removed_underline_skia_linux.png b/testing/resources/embedder_tests/bug_1893_removed_underline_skia_linux.png index 1d6d7663a3..5a92b77ae7 100644 Binary files a/testing/resources/embedder_tests/bug_1893_removed_underline_skia_linux.png and b/testing/resources/embedder_tests/bug_1893_removed_underline_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bug_1893_skia_linux.png b/testing/resources/embedder_tests/bug_1893_skia_linux.png index 52497d2ab6..fad3ff9c43 100644 Binary files a/testing/resources/embedder_tests/bug_1893_skia_linux.png and b/testing/resources/embedder_tests/bug_1893_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bug_410996566_skia.png b/testing/resources/embedder_tests/bug_410996566_skia.png index 379e056b78..e8d3703086 100644 Binary files a/testing/resources/embedder_tests/bug_410996566_skia.png and b/testing/resources/embedder_tests/bug_410996566_skia.png differ diff --git a/testing/resources/embedder_tests/bug_535730401_combobox_skia_linux.png b/testing/resources/embedder_tests/bug_535730401_combobox_skia_linux.png index 27952e9243..af631d6384 100644 Binary files a/testing/resources/embedder_tests/bug_535730401_combobox_skia_linux.png and b/testing/resources/embedder_tests/bug_535730401_combobox_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bug_535730401_skia_linux.png b/testing/resources/embedder_tests/bug_535730401_skia_linux.png index 7c0f98ef8a..18b8913895 100644 Binary files a/testing/resources/embedder_tests/bug_535730401_skia_linux.png and b/testing/resources/embedder_tests/bug_535730401_skia_linux.png differ diff --git a/testing/resources/embedder_tests/bug_889099_flattened_skia.png b/testing/resources/embedder_tests/bug_889099_flattened_skia.png index 039d46cf20..349b9a6ce4 100644 Binary files a/testing/resources/embedder_tests/bug_889099_flattened_skia.png and b/testing/resources/embedder_tests/bug_889099_flattened_skia.png differ diff --git a/testing/resources/embedder_tests/bug_889099_skia.png b/testing/resources/embedder_tests/bug_889099_skia.png index 5e6df9e3bb..4f2bb18889 100644 Binary files a/testing/resources/embedder_tests/bug_889099_skia.png and b/testing/resources/embedder_tests/bug_889099_skia.png differ diff --git a/testing/resources/embedder_tests/encrypted_skia_linux.png b/testing/resources/embedder_tests/encrypted_skia_linux.png index a22cc8639a..fc14bf2207 100644 Binary files a/testing/resources/embedder_tests/encrypted_skia_linux.png and b/testing/resources/embedder_tests/encrypted_skia_linux.png differ diff --git a/testing/resources/embedder_tests/focused_text_form_with_abc_skia_linux.png b/testing/resources/embedder_tests/focused_text_form_with_abc_skia_linux.png index ea055d8584..fcc0387a1f 100644 Binary files a/testing/resources/embedder_tests/focused_text_form_with_abc_skia_linux.png and b/testing/resources/embedder_tests/focused_text_form_with_abc_skia_linux.png differ diff --git a/testing/resources/embedder_tests/form_object_scaled_skia_linux.png b/testing/resources/embedder_tests/form_object_scaled_skia_linux.png index d491ee6455..45e421b468 100644 Binary files a/testing/resources/embedder_tests/form_object_scaled_skia_linux.png and b/testing/resources/embedder_tests/form_object_scaled_skia_linux.png differ diff --git a/testing/resources/embedder_tests/form_object_skia_linux.png b/testing/resources/embedder_tests/form_object_skia_linux.png index 7cfc2d043c..0984a3098d 100644 Binary files a/testing/resources/embedder_tests/form_object_skia_linux.png and b/testing/resources/embedder_tests/form_object_skia_linux.png differ diff --git a/testing/resources/embedder_tests/freetext_annotation_without_da_modified_skia_linux.png b/testing/resources/embedder_tests/freetext_annotation_without_da_modified_skia_linux.png index 9bb3766c64..c9320c9d5b 100644 Binary files a/testing/resources/embedder_tests/freetext_annotation_without_da_modified_skia_linux.png and b/testing/resources/embedder_tests/freetext_annotation_without_da_modified_skia_linux.png differ diff --git a/testing/resources/embedder_tests/freetext_annotation_without_da_skia_linux.png b/testing/resources/embedder_tests/freetext_annotation_without_da_skia_linux.png index e58455b6fe..653d5b8c1d 100644 Binary files a/testing/resources/embedder_tests/freetext_annotation_without_da_skia_linux.png and b/testing/resources/embedder_tests/freetext_annotation_without_da_skia_linux.png differ diff --git a/testing/resources/embedder_tests/goodbye_world_scaled_10_skia_linux.png b/testing/resources/embedder_tests/goodbye_world_scaled_10_skia_linux.png index e97e4eb783..8355c5f7b6 100644 Binary files a/testing/resources/embedder_tests/goodbye_world_scaled_10_skia_linux.png and b/testing/resources/embedder_tests/goodbye_world_scaled_10_skia_linux.png differ diff --git a/testing/resources/embedder_tests/goodbye_world_scaled_1_skia_linux.png b/testing/resources/embedder_tests/goodbye_world_scaled_1_skia_linux.png index 33a64163e5..f254079476 100644 Binary files a/testing/resources/embedder_tests/goodbye_world_scaled_1_skia_linux.png and b/testing/resources/embedder_tests/goodbye_world_scaled_1_skia_linux.png differ diff --git a/testing/resources/embedder_tests/goodbye_world_scaled_2_skia_linux.png b/testing/resources/embedder_tests/goodbye_world_scaled_2_skia_linux.png index 02a0166b38..f5728a40ad 100644 Binary files a/testing/resources/embedder_tests/goodbye_world_scaled_2_skia_linux.png and b/testing/resources/embedder_tests/goodbye_world_scaled_2_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_added_paths_skia_linux.png b/testing/resources/embedder_tests/hello_world_added_paths_skia_linux.png index 4deffb1ef7..14827f105d 100644 Binary files a/testing/resources/embedder_tests/hello_world_added_paths_skia_linux.png and b/testing/resources/embedder_tests/hello_world_added_paths_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_forced_dark_skia_linux.png b/testing/resources/embedder_tests/hello_world_forced_dark_skia_linux.png index 345272560d..8423fc5fbd 100644 Binary files a/testing/resources/embedder_tests/hello_world_forced_dark_skia_linux.png and b/testing/resources/embedder_tests/hello_world_forced_dark_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_lcd_skia_linux.png b/testing/resources/embedder_tests/hello_world_lcd_skia_linux.png index f3d082b552..9e248a536e 100644 Binary files a/testing/resources/embedder_tests/hello_world_lcd_skia_linux.png and b/testing/resources/embedder_tests/hello_world_lcd_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_no_smoothtext_skia_linux.png b/testing/resources/embedder_tests/hello_world_no_smoothtext_skia_linux.png index eb9a900bdd..e4bb9078e8 100644 Binary files a/testing/resources/embedder_tests/hello_world_no_smoothtext_skia_linux.png and b/testing/resources/embedder_tests/hello_world_no_smoothtext_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_removed_skia_linux.png b/testing/resources/embedder_tests/hello_world_removed_skia_linux.png index a58639db86..f0804e07d5 100644 Binary files a/testing/resources/embedder_tests/hello_world_removed_skia_linux.png and b/testing/resources/embedder_tests/hello_world_removed_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_scaled_10_skia_linux.png b/testing/resources/embedder_tests/hello_world_scaled_10_skia_linux.png index 9ffdd40e48..a58da9f23a 100644 Binary files a/testing/resources/embedder_tests/hello_world_scaled_10_skia_linux.png and b/testing/resources/embedder_tests/hello_world_scaled_10_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_scaled_1_skia_linux.png b/testing/resources/embedder_tests/hello_world_scaled_1_skia_linux.png index 28cb606885..682c003d00 100644 Binary files a/testing/resources/embedder_tests/hello_world_scaled_1_skia_linux.png and b/testing/resources/embedder_tests/hello_world_scaled_1_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_scaled_2_skia_linux.png b/testing/resources/embedder_tests/hello_world_scaled_2_skia_linux.png index fae96ef0ee..915aaea37f 100644 Binary files a/testing/resources/embedder_tests/hello_world_scaled_2_skia_linux.png and b/testing/resources/embedder_tests/hello_world_scaled_2_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_skia.png b/testing/resources/embedder_tests/hello_world_skia.png index f3d082b552..9e248a536e 100644 Binary files a/testing/resources/embedder_tests/hello_world_skia.png and b/testing/resources/embedder_tests/hello_world_skia.png differ diff --git a/testing/resources/embedder_tests/hello_world_split_streams_removed_hello_world_skia_linux.png b/testing/resources/embedder_tests/hello_world_split_streams_removed_hello_world_skia_linux.png index 08a4c85964..bdf9f5b655 100644 Binary files a/testing/resources/embedder_tests/hello_world_split_streams_removed_hello_world_skia_linux.png and b/testing/resources/embedder_tests/hello_world_split_streams_removed_hello_world_skia_linux.png differ diff --git a/testing/resources/embedder_tests/hello_world_with_changed_text_skia_linux.png b/testing/resources/embedder_tests/hello_world_with_changed_text_skia_linux.png index f98460bcb4..2150c9e9a0 100644 Binary files a/testing/resources/embedder_tests/hello_world_with_changed_text_skia_linux.png and b/testing/resources/embedder_tests/hello_world_with_changed_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/loaded_text_skia_linux.png b/testing/resources/embedder_tests/loaded_text_skia_linux.png index c957e32e65..a5c8591295 100644 Binary files a/testing/resources/embedder_tests/loaded_text_skia_linux.png and b/testing/resources/embedder_tests/loaded_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/save_multiple_fonts_skia_linux.png b/testing/resources/embedder_tests/save_multiple_fonts_skia_linux.png index 3a311c53db..8fdfab78c6 100644 Binary files a/testing/resources/embedder_tests/save_multiple_fonts_skia_linux.png and b/testing/resources/embedder_tests/save_multiple_fonts_skia_linux.png differ diff --git a/testing/resources/embedder_tests/save_new_text_skia_linux.png b/testing/resources/embedder_tests/save_new_text_skia_linux.png index fec8aa3a33..924fe264ed 100644 Binary files a/testing/resources/embedder_tests/save_new_text_skia_linux.png and b/testing/resources/embedder_tests/save_new_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/split_streams_removed_stream1_skia_linux.png b/testing/resources/embedder_tests/split_streams_removed_stream1_skia_linux.png index 26468ff156..726e0fa4e0 100644 Binary files a/testing/resources/embedder_tests/split_streams_removed_stream1_skia_linux.png and b/testing/resources/embedder_tests/split_streams_removed_stream1_skia_linux.png differ diff --git a/testing/resources/embedder_tests/stamp_with_color_scheme_skia_linux.png b/testing/resources/embedder_tests/stamp_with_color_scheme_skia_linux.png index e6ab1f6e13..6b70c3cd88 100644 Binary files a/testing/resources/embedder_tests/stamp_with_color_scheme_skia_linux.png and b/testing/resources/embedder_tests/stamp_with_color_scheme_skia_linux.png differ diff --git a/testing/resources/embedder_tests/text_form_no_highlight_skia_linux.png b/testing/resources/embedder_tests/text_form_no_highlight_skia_linux.png index 5e9c43a6c4..94ed1ced92 100644 Binary files a/testing/resources/embedder_tests/text_form_no_highlight_skia_linux.png and b/testing/resources/embedder_tests/text_form_no_highlight_skia_linux.png differ diff --git a/testing/resources/embedder_tests/text_form_skia_linux.png b/testing/resources/embedder_tests/text_form_skia_linux.png index ca89ce3b24..55b65c2dd4 100644 Binary files a/testing/resources/embedder_tests/text_form_skia_linux.png and b/testing/resources/embedder_tests/text_form_skia_linux.png differ diff --git a/testing/resources/embedder_tests/text_in_page_marked_non_primes_skia_linux.png b/testing/resources/embedder_tests/text_in_page_marked_non_primes_skia_linux.png index bea4e2bc2d..ece8b7f573 100644 Binary files a/testing/resources/embedder_tests/text_in_page_marked_non_primes_skia_linux.png and b/testing/resources/embedder_tests/text_in_page_marked_non_primes_skia_linux.png differ diff --git a/testing/resources/embedder_tests/text_in_page_marked_skia_linux.png b/testing/resources/embedder_tests/text_in_page_marked_skia_linux.png index d3050d2460..a8ada6fa13 100644 Binary files a/testing/resources/embedder_tests/text_in_page_marked_skia_linux.png and b/testing/resources/embedder_tests/text_in_page_marked_skia_linux.png differ diff --git a/testing/resources/embedder_tests/truetype_font_text_skia_linux.png b/testing/resources/embedder_tests/truetype_font_text_skia_linux.png index 7fbef11daa..b9e1d3a09d 100644 Binary files a/testing/resources/embedder_tests/truetype_font_text_skia_linux.png and b/testing/resources/embedder_tests/truetype_font_text_skia_linux.png differ diff --git a/testing/resources/embedder_tests/unfocused_text_form_with_abc_skia_linux.png b/testing/resources/embedder_tests/unfocused_text_form_with_abc_skia_linux.png index 72565b7566..048a59a4e0 100644 Binary files a/testing/resources/embedder_tests/unfocused_text_form_with_abc_skia_linux.png and b/testing/resources/embedder_tests/unfocused_text_form_with_abc_skia_linux.png differ diff --git a/testing/resources/named_dests_old_style_indirect.in b/testing/resources/named_dests_old_style_indirect.in new file mode 100644 index 0000000000..80dcfb5239 --- /dev/null +++ b/testing/resources/named_dests_old_style_indirect.in @@ -0,0 +1,52 @@ +{{header}} +{{object 1 0}} << + /Type /Catalog + /Pages 2 0 R + /Dests 6 0 R +>> +endobj +{{object 2 0}} << + /Type /Pages + /Count 3 + /Kids [3 0 R 4 0 R 5 0 R] +>> +endobj +{{object 3 0}} << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] +>> +endobj +{{object 4 0}} << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] +>> +endobj +{{object 5 0}} << + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] +>> +endobj +% Old-style top-level Dests dictionary whose values are all indirect. +% The keys are listed in sorted order, which is their index order. +% IndirectDangling intentionally references non-existent object 999. +{{object 6 0}} << + /IndirectArray 7 0 R + /IndirectDangling 999 0 R + /IndirectDict 8 0 R +>> +endobj +% Destination array for the second page. +{{object 7 0}} [4 0 R /XYZ 0 792 0] +endobj +% Destination dictionary for the third page. +{{object 8 0}} << + /D [5 0 R /Fit] +>> +endobj +{{xref}} +{{trailer}} +{{startxref}} +%%EOF diff --git a/testing/resources/named_dests_old_style_indirect.pdf b/testing/resources/named_dests_old_style_indirect.pdf new file mode 100644 index 0000000000..38eea82eae Binary files /dev/null and b/testing/resources/named_dests_old_style_indirect.pdf differ diff --git a/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png index 0077178d0c..ed4fd40359 100644 Binary files a/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png b/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png index 6e382c970e..c3d41bc001 100644 Binary files a/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png index 1732c2d84d..0882a56608 100644 Binary files a/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_1388_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_skia.pdf.0.png index 29b9e3c388..40c79629e3 100644 Binary files a/testing/resources/pixel/bug_1388_2_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_1388_2_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_432208037_expected_skia.pdf.0.png b/testing/resources/pixel/bug_432208037_expected_skia.pdf.0.png index a044b9d514..c85acb0bcc 100644 Binary files a/testing/resources/pixel/bug_432208037_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_432208037_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_477200528_expected_skia.pdf.0.png b/testing/resources/pixel/bug_477200528_expected_skia.pdf.0.png index 4fbe7d093c..27229e54a9 100644 Binary files a/testing/resources/pixel/bug_477200528_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_477200528_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_524043_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_524043_1_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_524043_1_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_524043_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_skia.pdf.0.png index 3b453177f8..d2c589ef95 100644 Binary files a/testing/resources/pixel/bug_524043_2_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_524043_2_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_524043_3_expected_skia.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_524043_3_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_524043_3_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_524043_4_expected_skia.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_524043_4_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_524043_4_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_524043_5_expected_skia.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_524043_5_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_524043_5_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_524043_7_expected_skia.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_skia.pdf.0.png index 3b453177f8..d2c589ef95 100644 Binary files a/testing/resources/pixel/bug_524043_7_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_524043_7_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_528103_expected_skia.pdf.0.png b/testing/resources/pixel/bug_528103_expected_skia.pdf.0.png index 464ca92519..4c96129938 100644 Binary files a/testing/resources/pixel/bug_528103_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_528103_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_543018_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_543018_1_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_543018_1_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_543018_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_543018_2_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_543018_2_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_551258_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_skia.pdf.0.png index 7e8ec39695..c019b803d7 100644 Binary files a/testing/resources/pixel/bug_551258_1_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_551258_1_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png b/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png index 51f80f6c36..f7b1eb913c 100644 Binary files a/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png b/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png index faefe10463..737f0e8c70 100644 Binary files a/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png index 9cd45ef3ff..1bc6095c42 100644 Binary files a/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png index 4ebd784bb5..1ff390e854 100644 Binary files a/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_820345_expected_skia.pdf.0.png b/testing/resources/pixel/bug_820345_expected_skia.pdf.0.png index f3d082b552..9e248a536e 100644 Binary files a/testing/resources/pixel/bug_820345_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_820345_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/bug_909762_expected_skia_linux.pdf.0.png b/testing/resources/pixel/bug_909762_expected_skia_linux.pdf.0.png index abac6e4fac..c945d5d72a 100644 Binary files a/testing/resources/pixel/bug_909762_expected_skia_linux.pdf.0.png and b/testing/resources/pixel/bug_909762_expected_skia_linux.pdf.0.png differ diff --git a/testing/resources/pixel/bug_925736_expected_skia.pdf.0.png b/testing/resources/pixel/bug_925736_expected_skia.pdf.0.png index 58a43bb0fe..bdbb98433d 100644 Binary files a/testing/resources/pixel/bug_925736_expected_skia.pdf.0.png and b/testing/resources/pixel/bug_925736_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/font_size_expected_skia.pdf.0.png b/testing/resources/pixel/font_size_expected_skia.pdf.0.png index 8f33bc6d27..5013cb08c8 100644 Binary files a/testing/resources/pixel/font_size_expected_skia.pdf.0.png and b/testing/resources/pixel/font_size_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/form_textfield_focused_ltr_expected_skia.pdf.0.png b/testing/resources/pixel/form_textfield_focused_ltr_expected_skia.pdf.0.png index 9f4dd11682..2a02e79832 100644 Binary files a/testing/resources/pixel/form_textfield_focused_ltr_expected_skia.pdf.0.png and b/testing/resources/pixel/form_textfield_focused_ltr_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/form_textfield_focused_rtl_expected_skia.pdf.0.png b/testing/resources/pixel/form_textfield_focused_rtl_expected_skia.pdf.0.png index 64d4cec8ed..e11763c59e 100644 Binary files a/testing/resources/pixel/form_textfield_focused_rtl_expected_skia.pdf.0.png and b/testing/resources/pixel/form_textfield_focused_rtl_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/form_textfield_selected_ltr_expected_skia.pdf.0.png b/testing/resources/pixel/form_textfield_selected_ltr_expected_skia.pdf.0.png index a89936b88c..c6f9db80ea 100644 Binary files a/testing/resources/pixel/form_textfield_selected_ltr_expected_skia.pdf.0.png and b/testing/resources/pixel/form_textfield_selected_ltr_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/form_textfield_selected_rtl_expected_skia.pdf.0.png b/testing/resources/pixel/form_textfield_selected_rtl_expected_skia.pdf.0.png index 126cb8506b..fd10e7c393 100644 Binary files a/testing/resources/pixel/form_textfield_selected_rtl_expected_skia.pdf.0.png and b/testing/resources/pixel/form_textfield_selected_rtl_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/freetext_annotation_without_da_expected_skia.pdf.0.png b/testing/resources/pixel/freetext_annotation_without_da_expected_skia.pdf.0.png index e58455b6fe..653d5b8c1d 100644 Binary files a/testing/resources/pixel/freetext_annotation_without_da_expected_skia.pdf.0.png and b/testing/resources/pixel/freetext_annotation_without_da_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/generation_numbers1_expected_skia.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_skia.pdf.0.png index c782b65840..0f4bc5dd4b 100644 Binary files a/testing/resources/pixel/generation_numbers1_expected_skia.pdf.0.png and b/testing/resources/pixel/generation_numbers1_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/generation_numbers2_expected_skia.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_skia.pdf.0.png index c782b65840..0f4bc5dd4b 100644 Binary files a/testing/resources/pixel/generation_numbers2_expected_skia.pdf.0.png and b/testing/resources/pixel/generation_numbers2_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png b/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png index fa5232c779..95f067be7b 100644 Binary files a/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png and b/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png b/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png index 16486a272b..d07b08228a 100644 Binary files a/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png and b/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png differ diff --git a/testing/resources/pixel/text_form_multiline_expected_skia.pdf.0.png b/testing/resources/pixel/text_form_multiline_expected_skia.pdf.0.png index 4ea13243d4..4e53604561 100644 Binary files a/testing/resources/pixel/text_form_multiline_expected_skia.pdf.0.png and b/testing/resources/pixel/text_form_multiline_expected_skia.pdf.0.png differ diff --git a/testing/test.gni b/testing/test.gni index 6ad2c2d4ab..9b98489077 100644 --- a/testing/test.gni +++ b/testing/test.gni @@ -42,7 +42,8 @@ template("test") { "$target_out_dir/${invoker.target_name}/${invoker.target_name}" executable(_exec_target) { - # Configs will always be defined since we set_defaults in BUILDCONFIG.gn. + # Configs will always be defined since we set_defaults in + # BUILDCONFIG.gn. configs = [] data_deps = [] forward_variables_from(invoker, @@ -94,6 +95,7 @@ template("test") { if (defined(invoker.add_unwind_tables_in_apk) && invoker.add_unwind_tables_in_apk) { _unwind_table_asset_name = "${target_name}_unwind_assets" + unwind_table_asset(_unwind_table_asset_name) { testonly = true library_target = _library_target @@ -102,7 +104,8 @@ template("test") { } shared_library(_library_target) { - # Configs will always be defined since we set_defaults in BUILDCONFIG.gn. + # Configs will always be defined since we set_defaults in + # BUILDCONFIG.gn. configs = [] # Prevent list overwriting warning. configs = invoker.configs testonly = true @@ -118,6 +121,7 @@ template("test") { deps += [ "//testing/android/native_test:native_test_native_code" ] } } + unittest_apk(_apk_target) { forward_variables_from(invoker, _apk_specific_vars) shared_library = ":$_library_target" @@ -260,6 +264,9 @@ template("pdfium_unittest_source_set") { testonly = true sources = invoker.sources + + # Explicitly specifying public disables implictly public headers in sources. + public = [] configs += [ _pdfium_root_dir + ":pdfium_core_config" ] if (defined(invoker.configs)) { configs += invoker.configs @@ -279,6 +286,9 @@ template("pdfium_embeddertest_source_set") { testonly = true sources = invoker.sources + + # Explicitly specifying public disables implictly public headers in sources. + public = [] configs += [ _pdfium_root_dir + ":pdfium_core_config" ] if (defined(invoker.configs)) { configs += invoker.configs diff --git a/testing/tools/PRESUBMIT.py b/testing/tools/PRESUBMIT.py index e76673f9c0..1066f47962 100644 --- a/testing/tools/PRESUBMIT.py +++ b/testing/tools/PRESUBMIT.py @@ -14,6 +14,9 @@ def _CommonChecks(input_api, output_api): tests = [] tests.extend( input_api.canned_checks.GetPylint(input_api, output_api, version='2.7')) + tests.extend( + input_api.canned_checks.GetUnitTestsInDirectory( + input_api, output_api, '.', files_to_check=[r'^.+_test\.py$'])) return tests diff --git a/testing/tools/libclang_check.py b/testing/tools/libclang_check.py new file mode 100755 index 0000000000..de8958bf5b --- /dev/null +++ b/testing/tools/libclang_check.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# 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. +"""Verifies rust-libclang and rust-toolchain revisions are in sync in DEPS.""" + +import re +import sys + + +def _ExtractRevisions(deps_content, regex): + """Gets the revisions by searching the lines in deps_content using regex.""" + revisions = [] + for line in deps_content.splitlines(): + match = regex.match(line) + if not match: + continue + revisions.append((match.group(1), match.group(2))) + return revisions + + +def _GetLibclangRevisions(deps_content): + """Gets the rust-libclang revisions from DEPS content.""" + regex = re.compile( + r"^ 'object_name': '(.*)/rust-libclang-(.*)\.tar\.xz',$") + return _ExtractRevisions(deps_content, regex) + + +def _GetRustToolchainRevisions(deps_content): + """Gets the rust-toolchain revisions from DEPS content.""" + regex = re.compile( + r"^ 'object_name': '(.*)/rust-toolchain-(.*)\.tar\.xz',$") + return _ExtractRevisions(deps_content, regex) + + +def CheckDeps(deps_content): + """Checks that rust-libclang and rust-toolchain revisions match in DEPS.""" + libclang_revisions = _GetLibclangRevisions(deps_content) + if not libclang_revisions: + return 'Cannot parse rust-libclang revisions' + + rust_revisions = _GetRustToolchainRevisions(deps_content) + if not rust_revisions: + return 'Cannot parse rust-toolchain revisions' + + if libclang_revisions != rust_revisions: + return ('Mismatch between rust-libclang and rust-toolchain: ' + f'{libclang_revisions} vs. {rust_revisions}') + return None + + +def main(): + if len(sys.argv) != 2: + print('Wrong number of arguments') + return 0 + + deps_path = sys.argv[1] + with open(deps_path, 'r', encoding='utf-8') as f: + deps_content = f.read() + + error = CheckDeps(deps_content) + if error: + print(f'{deps_path}: {error}') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/testing/tools/libclang_check_test.py b/testing/tools/libclang_check_test.py new file mode 100755 index 0000000000..b9c4b2f86c --- /dev/null +++ b/testing/tools/libclang_check_test.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# 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. +"""Tests for libclang_check.""" + +import unittest + +from libclang_check import CheckDeps + + +class LibclangCheckTest(unittest.TestCase): + + def testSuccess(self): + deps = ''' +deps = { + 'third_party/llvm-libclang': { + 'objects': [ + { + 'object_name': 'Linux_x64/rust-libclang-1.tar.xz', + }, + { + 'object_name': 'Win/rust-libclang-1.tar.xz', + }, + ], + }, + 'third_party/rust-toolchain': { + 'objects': [ + { + 'object_name': 'Linux_x64/rust-toolchain-1.tar.xz', + }, + { + 'object_name': 'Win/rust-toolchain-1.tar.xz', + }, + ], + }, +} +''' + self.assertIsNone(CheckDeps(deps)) + + def testRevisionMismatch(self): + deps = ''' +deps = { + 'third_party/llvm-libclang': { + 'objects': [ + { + 'object_name': 'Linux_x64/rust-libclang-1.tar.xz', + }, + ], + }, + 'third_party/rust-toolchain': { + 'objects': [ + { + 'object_name': 'Linux_x64/rust-toolchain-2.tar.xz', + }, + ], + }, +} +''' + self.assertEqual( + 'Mismatch between rust-libclang and rust-toolchain: ' + "[('Linux_x64', '1')] vs. [('Linux_x64', '2')]", CheckDeps(deps)) + + def testMissingLibclang(self): + deps = ''' +deps = { + 'third_party/rust-toolchain': { + 'objects': [ + { + 'object_name': 'Linux_x64/rust-toolchain-1.tar.xz', + }, + ], + }, +} +''' + self.assertEqual('Cannot parse rust-libclang revisions', CheckDeps(deps)) + + def testMissingRustToolchain(self): + deps = ''' +deps = { + 'third_party/llvm-libclang': { + 'objects': [ + { + 'object_name': 'Linux_x64/rust-libclang-1.tar.xz', + }, + ], + }, +} +''' + self.assertEqual('Cannot parse rust-toolchain revisions', CheckDeps(deps)) + + +if __name__ == '__main__': + unittest.main() diff --git a/testing/tools/pngdiffer.py b/testing/tools/pngdiffer.py index c180ebebc4..f31cfecd4a 100755 --- a/testing/tools/pngdiffer.py +++ b/testing/tools/pngdiffer.py @@ -34,6 +34,8 @@ class DiffMetrics: max_delta: int mse: float win_mse: float + mean_ssim: float = 1.0 + min_window_ssim: float = 1.0 @property def dims_match(self): @@ -202,7 +204,9 @@ def _RunImageCompareCommand(self, image_diff, image_matching_algorithm): total=int(kv['total']), max_delta=int(kv['max_delta']), mse=float(kv['mse']), - win_mse=float(kv['win_mse'])) + win_mse=float(kv['win_mse']), + mean_ssim=float(kv.get('mean_ssim', 1.0)), + min_window_ssim=float(kv.get('min_window_ssim', 1.0))) image_diff.metrics = metrics matched, failure_reason = self.EvaluateMatch(metrics, diff --git a/testing/utils/bitmap_saver.cpp b/testing/utils/bitmap_saver.cpp index 90689b31d0..3b6d9d26b0 100644 --- a/testing/utils/bitmap_saver.cpp +++ b/testing/utils/bitmap_saver.cpp @@ -16,7 +16,7 @@ #include "core/fxcrt/notreached.h" #include "testing/png_codec/png_codec.h" -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA #include "third_party/skia/include/core/SkAlphaType.h" // nogncheck #include "third_party/skia/include/core/SkColorType.h" // nogncheck #include "third_party/skia/include/core/SkImageInfo.h" // nogncheck @@ -54,7 +54,7 @@ std::vector BitmapSaver::EncodeBitmapToPng(FPDF_BITMAP bitmap) { case FPDFBitmap_BGRA: return png_codec::EncodeBGRA(input, width, height, stride, /*discard_transparency=*/false); -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA case FPDFBitmap_BGRA_Premul: { std::vector straight_alpha_input = ConvertToStraightAlpha(input, width, height, stride); @@ -89,7 +89,7 @@ void BitmapSaver::WriteBitmapToPng(CFX_DIBitmap* bitmap, WriteBitmapToPng(reinterpret_cast(bitmap), filename); } -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA // static std::vector BitmapSaver::ConvertToStraightAlpha( pdfium::span input, diff --git a/testing/utils/bitmap_saver.h b/testing/utils/bitmap_saver.h index 1111b154f8..f8d80252c0 100644 --- a/testing/utils/bitmap_saver.h +++ b/testing/utils/bitmap_saver.h @@ -21,7 +21,7 @@ class BitmapSaver { static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename); static void WriteBitmapToPng(CFX_DIBitmap* bitmap, const std::string& filename); -#ifdef PDF_ENABLE_SKIA +#ifdef PDF_USE_SKIA static std::vector ConvertToStraightAlpha( pdfium::span input, int width, diff --git a/testing/utils/pixel_diff_util.cpp b/testing/utils/pixel_diff_util.cpp index a9531fe536..36a12f194a 100644 --- a/testing/utils/pixel_diff_util.cpp +++ b/testing/utils/pixel_diff_util.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace { @@ -133,6 +134,119 @@ double CalculateMaxWindowMSE(pdfium::span baseline, return max_win_mse; } +SSIMMetrics CalculateSSIM(pdfium::span baseline, + size_t baseline_stride_pixels, + pdfium::span actual, + size_t actual_stride_pixels, + int w, + int h) { + if (w <= 0 || h <= 0) { + return {0.0, 0.0}; + } + + if (baseline_stride_pixels < static_cast(w) || + actual_stride_pixels < static_cast(w)) { + return {0.0, 0.0}; + } + + const size_t min_baseline_size = + (static_cast(h) - 1) * baseline_stride_pixels + w; + const size_t min_actual_size = + (static_cast(h) - 1) * actual_stride_pixels + w; + if (baseline.size() < min_baseline_size || actual.size() < min_actual_size) { + return {0.0, 0.0}; + } + + constexpr double kC1 = 6.5025; // (0.01 * 255)^2 + constexpr double kC2 = 58.5225; // (0.03 * 255)^2 + + double ssim_total = 0.0; + double min_window_ssim = 1.0; + int samples = 0; + + constexpr int kWindowSize = 8; + constexpr int kStepSize = 4; + + const int win_w = std::min(w, kWindowSize); + const int win_h = std::min(h, kWindowSize); + + for (int y = 0; y < h; y += kStepSize) { + if (y > h - win_h) { + y = h - win_h; + } + for (int x = 0; x < w; x += kStepSize) { + if (x > w - win_w) { + x = w - win_w; + } + + std::array sum_s = {}; + std::array sum_r = {}; + std::array sum_sq_s = {}; + std::array sum_sq_r = {}; + std::array sum_sxr = {}; + + for (int wy = 0; wy < win_h; ++wy) { + const size_t b_offset = (y + wy) * baseline_stride_pixels; + const size_t a_offset = (y + wy) * actual_stride_pixels; + for (int wx = 0; wx < win_w; ++wx) { + UnpackedPixel p_b(baseline[b_offset + x + wx]); + UnpackedPixel p_a(actual[a_offset + x + wx]); + + const std::array b_channels = {p_b.red, p_b.green, + p_b.blue}; + const std::array a_channels = {p_a.red, p_a.green, + p_a.blue}; + + for (int c = 0; c < 3; ++c) { + sum_s[c] += b_channels[c]; + sum_r[c] += a_channels[c]; + sum_sq_s[c] += b_channels[c] * b_channels[c]; + sum_sq_r[c] += a_channels[c] * a_channels[c]; + sum_sxr[c] += b_channels[c] * a_channels[c]; + } + } + } + + double window_ssim = 0.0; + const int count = win_w * win_h; + + for (int c = 0; c < 3; ++c) { + const double mean_s = static_cast(sum_s[c]) / count; + const double mean_r = static_cast(sum_r[c]) / count; + + const double var_s = + static_cast(sum_sq_s[c]) / count - mean_s * mean_s; + const double var_r = + static_cast(sum_sq_r[c]) / count - mean_r * mean_r; + const double covar_sxr = + static_cast(sum_sxr[c]) / count - mean_s * mean_r; + + const double ssim_n = + (2.0 * mean_s * mean_r + kC1) * (2.0 * covar_sxr + kC2); + const double ssim_d = + (mean_s * mean_s + mean_r * mean_r + kC1) * (var_s + var_r + kC2); + + window_ssim += ssim_n / ssim_d; + } + + const double final_window_ssim = window_ssim / 3.0; + ssim_total += final_window_ssim; + min_window_ssim = std::min(min_window_ssim, final_window_ssim); + samples++; + + if (x == w - win_w) { + break; + } + } + if (y == h - win_h) { + break; + } + } + + return samples > 0 ? SSIMMetrics{ssim_total / samples, min_window_ssim} + : SSIMMetrics{1.0, 1.0}; +} + int CalculatePixelsDifferent(pdfium::span baseline, size_t baseline_stride_pixels, pdfium::span actual, diff --git a/testing/utils/pixel_diff_util.h b/testing/utils/pixel_diff_util.h index c15e6366ce..7aa153a69e 100644 --- a/testing/utils/pixel_diff_util.h +++ b/testing/utils/pixel_diff_util.h @@ -20,8 +20,8 @@ inline constexpr double kMaxFuzzyWindowMeanSquaredError = 15.0; // Fontations fuzzy matching limits: allows a larger per-channel delta to // accommodate subpixel curve antialiasing variances while enforcing a tight // mean squared error bound. -inline constexpr uint8_t kMaxFontationsPixelDelta = 10; -inline constexpr double kMaxFontationsMeanSquaredError = 0.10; +inline constexpr uint8_t kMaxFontationsPixelDelta = 12; +inline constexpr double kMaxFontationsMeanSquaredError = 0.20; inline constexpr int kMaxFontationsWindowSize = kMaxFuzzyWindowSize; inline constexpr double kMaxFontationsWindowMeanSquaredError = kMaxFuzzyWindowMeanSquaredError; @@ -48,6 +48,11 @@ inline constexpr DiffOptions kFontationsDiffOptions = { .max_window_mean_squared_error = kMaxFontationsWindowMeanSquaredError, }; +struct SSIMMetrics { + double mean_ssim; + double min_window_ssim; +}; + // Returns the largest difference in pixel channels between `baseline_pixel` and // `actual_pixel`. Pixels are expected to be in 32-bit ARGB or BGRA format. uint8_t MaxPixelPerChannelDelta(uint32_t baseline_pixel, uint32_t actual_pixel); @@ -68,6 +73,18 @@ double CalculateMaxWindowMSE(pdfium::span baseline, int h, int window_size); +// Computes the Structural Similarity (SSIM) index across the RGB channels. +// Uses an 8x8 sliding window with a step size of 4 pixels. Returns both the +// global mean SSIM, and the minimum local window SSIM (worst-case match). +// Values are between 0.0 and 1.0, where 1.0 indicates perfect similarity. +// See https://en.wikipedia.org/wiki/Structural_similarity_index_measure +SSIMMetrics CalculateSSIM(pdfium::span baseline, + size_t baseline_stride_pixels, + pdfium::span actual, + size_t actual_stride_pixels, + int w, + int h); + // Returns the number of differing pixels between `baseline` and `actual` within // the overlap region of dimensions (`w`, `h`), taking into account maximum // per-channel delta, global mean squared error, and optional windowed mean diff --git a/testing/utils/pixel_diff_util_unittest.cpp b/testing/utils/pixel_diff_util_unittest.cpp index cc60693556..13509ab041 100644 --- a/testing/utils/pixel_diff_util_unittest.cpp +++ b/testing/utils/pixel_diff_util_unittest.cpp @@ -129,6 +129,29 @@ TEST(PixelDiffUtilTest, CalculateMaxWindowMSEInvalidInputs) { EXPECT_DOUBLE_EQ(0.0, CalculateMaxWindowMSE(img, 4, img, 4, 4, 4, 0)); } +TEST(PixelDiffUtilTest, CalculateSSIMExactMatch) { + std::vector img(64, 0xffffffff); + EXPECT_DOUBLE_EQ(1.0, CalculateSSIM(img, 8, img, 8, 8, 8).mean_ssim); + EXPECT_DOUBLE_EQ(1.0, CalculateSSIM(img, 8, img, 8, 8, 8).min_window_ssim); +} + +TEST(PixelDiffUtilTest, CalculateSSIMReallyOff) { + std::vector img1(64, 0xffffffff); + std::vector img2(img1); + // Corrupt a few pixels. + img2[12] = 56; + img2[34] = 78; + EXPECT_NEAR(0.0364, CalculateSSIM(img1, 8, img2, 8, 8, 8).mean_ssim, 0.0001); + EXPECT_NEAR(0.0364, CalculateSSIM(img1, 8, img2, 8, 8, 8).min_window_ssim, + 0.0001); +} + +TEST(PixelDiffUtilTest, CalculateSSIMInvalidInputs) { + std::vector img(16, 0); + EXPECT_DOUBLE_EQ(0.0, CalculateSSIM(img, 0, img, 0, 0, 0).mean_ssim); + EXPECT_DOUBLE_EQ(0.0, CalculateSSIM(img, 0, img, 0, 0, 0).min_window_ssim); +} + TEST(PixelDiffUtilTest, CalculatePixelsDifferentExactMatch) { std::vector img(64, 0xffffffff); EXPECT_EQ(0, diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn index 2872699033..8f71edba04 100644 --- a/third_party/BUILD.gn +++ b/third_party/BUILD.gn @@ -38,7 +38,7 @@ if (pdf_enable_xfa) { } } -if (pdf_bundle_freetype) { +if (pdf_enable_freetype && pdf_bundle_freetype) { config("freetype_public_includes_config") { # The relative freetype/include path points to PDFium's custom config. # The absolute path points to whatever copy of FreeType is in @@ -259,7 +259,7 @@ if (pdf_use_agg) { "agg23/agg_vcgen_stroke.h", "agg23/agg_vertex_sequence.h", ] - deps = [ "../core/fxcrt" ] + public_deps = [ "../core/fxcrt" ] } } @@ -355,6 +355,11 @@ if (!build_with_chromium) { public_deps = [ "//third_party/libjpeg" ] } } + + # Stub target for //third_party/libyuv, which references + # //third_party:jpeg_includes when libyuv_disable_jpeg is false. + group("jpeg_includes") { + } } config("system_zlib_config") { diff --git a/third_party/agg23/0018-outline_aa_reset.patch b/third_party/agg23/0018-outline_aa_reset.patch new file mode 100644 index 0000000000..7d9c8d63fd --- /dev/null +++ b/third_party/agg23/0018-outline_aa_reset.patch @@ -0,0 +1,20 @@ +diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp +index ed59cf0ffb..d561e02b58 100644 +--- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp ++++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp +@@ -101,13 +101,13 @@ outline_aa::outline_aa() : + m_max_y(-0x7FFFFFFF), + m_sorted(false) + { +- m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); ++ m_cur_cell.set(0x7FFFFFFF, 0x7FFFFFFF, 0, 0); + } + void outline_aa::reset() + { + m_num_cells = 0; + m_cur_block = 0; +- m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); ++ m_cur_cell.set(0x7FFFFFFF, 0x7FFFFFFF, 0, 0); + m_sorted = false; + m_min_x = 0x7FFFFFFF; + m_min_y = 0x7FFFFFFF; diff --git a/third_party/agg23/0018-reuse-across-paths.patch b/third_party/agg23/0019-reuse-across-paths.patch similarity index 98% rename from third_party/agg23/0018-reuse-across-paths.patch rename to third_party/agg23/0019-reuse-across-paths.patch index 75f2831a14..bca64a25ca 100644 --- a/third_party/agg23/0018-reuse-across-paths.patch +++ b/third_party/agg23/0019-reuse-across-paths.patch @@ -18,7 +18,7 @@ index 82148061a..d04e941fc 100644 void allocate_block(unsigned nb); unsigned char* storage_ptrs(float** xy_ptr); diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp -index ed59cf0ff..0e98b908a 100644 +index d561e02b5..dad4715bb 100644 --- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp +++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp @@ -134,7 +134,11 @@ AGG_INLINE void outline_aa::add_cur_cell() diff --git a/third_party/agg23/README.pdfium b/third_party/agg23/README.pdfium index 201d546f2f..e00294eba3 100644 --- a/third_party/agg23/README.pdfium +++ b/third_party/agg23/README.pdfium @@ -39,7 +39,8 @@ outline_aa::render_line(). 0016-support-negative-dash-phases.patch: Support negative dash phases in calc_dash_start(). 0017-support-odd-size-dash-arrays.patch: Support odd-sized dash arrays. -0018-reuse-across-paths.patch: Let one path_storage and one +0018-outline_aa_reset.patch: Correctly reset current cell. +0019-reuse-across-paths.patch: Let one path_storage and one rasterizer_scanline_aa draw many paths: path_storage::remove_all(), rasterizer_scanline_aa::reset_to_initial(), and a cell block limit that counts the blocks the current outline uses rather than the ones allocated. diff --git a/third_party/agg23/agg_rasterizer_scanline_aa.cpp b/third_party/agg23/agg_rasterizer_scanline_aa.cpp index 0e98b908a2..dad4715bba 100644 --- a/third_party/agg23/agg_rasterizer_scanline_aa.cpp +++ b/third_party/agg23/agg_rasterizer_scanline_aa.cpp @@ -101,13 +101,13 @@ outline_aa::outline_aa() : m_max_y(-0x7FFFFFFF), m_sorted(false) { - m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); + m_cur_cell.set(0x7FFFFFFF, 0x7FFFFFFF, 0, 0); } void outline_aa::reset() { m_num_cells = 0; m_cur_block = 0; - m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); + m_cur_cell.set(0x7FFFFFFF, 0x7FFFFFFF, 0, 0); m_sorted = false; m_min_x = 0x7FFFFFFF; m_min_y = 0x7FFFFFFF; diff --git a/third_party/fast_float/README.pdfium b/third_party/fast_float/README.pdfium index fc5b71154a..18139a318b 100644 --- a/third_party/fast_float/README.pdfium +++ b/third_party/fast_float/README.pdfium @@ -1,7 +1,7 @@ Name: fast_float URL: https://github.com/fastfloat/fast_float -Version: 7.0.0 -Revision: cb1d42aaa1e14b09e1452cfdef373d051b8c02a4 +Version: upstream/v8.3.0 +Revision: b0ab987b3dfdde13fa1915f65ef2a5c068d9208c Update Mechanism: Manual License: MIT License File: //third_party/fast_float/src/LICENSE-MIT diff --git a/third_party/freetype/README.pdfium b/third_party/freetype/README.pdfium index 43e7bf72c9..613e495c27 100644 --- a/third_party/freetype/README.pdfium +++ b/third_party/freetype/README.pdfium @@ -1,7 +1,7 @@ Name: FreeType URL: https://gitlab.freedesktop.org/freetype/freetype -Version: VER-2-14-3-85 -Revision: 4800589f76dc62fbe523a74a59e0d4850a5dd7df +Version: VER-2-14-3-110 +Revision: 891f904e2e7b4289f615598cd4b69832f87d2f3a Update Mechanism: Manual CPEPrefix: cpe:/a:freetype:freetype:2.14.3 Security Critical: yes diff --git a/third_party/harfbuzz/README.pdfium b/third_party/harfbuzz/README.pdfium index c3024ae5c0..7daed24284 100644 --- a/third_party/harfbuzz/README.pdfium +++ b/third_party/harfbuzz/README.pdfium @@ -1,10 +1,10 @@ Name: harfbuzz Short Name: harfbuzz URL: http://harfbuzz.org -Version: 14.2.0-11 -CPEPrefix: cpe:/a:harfbuzz_project:harfbuzz:14.2.0 -Date: 2026-04-29 -Revision: e6741e2205309752839da60ff075b7fa2e7cddd3 +Version: 14.5.0-10 +CPEPrefix: cpe:/a:harfbuzz_project:harfbuzz:14.5.0 +Date: 2026-09-29 +Revision: ff36703b95013ec1d437e6038e1aa7c881275f4e Update Mechanism: Manual Security Critical: yes Shipped: yes diff --git a/tools/roll_pdfium_deps.py b/tools/roll_pdfium_deps.py index f72b1a4eb8..6fb3684776 100755 --- a/tools/roll_pdfium_deps.py +++ b/tools/roll_pdfium_deps.py @@ -41,6 +41,9 @@ 'siso_version', 'skia_revision', 'testing_rust_revision', + 'third_party/llvm-build/Release+Asserts', + 'third_party/llvm-libclang', + 'third_party/rust-toolchain', 'tools_rust_revision', 'tools_win_revision', 'v8_revision', @@ -88,10 +91,46 @@ def get_revision_from_dep_value(dep_value): return None -def roll_dep_impl(chromium_deps_parser, pdfium_deps_parser, deps_entry): +def roll_gcs_dep_impl(chromium_deps_parser, pdfium_deps_parser, deps_entry, + chromium_deps_file, pdfium_deps_file): + pdfium_dep_value = pdfium_deps_parser.deps.get(deps_entry) + pdfium_objects = pdfium_dep_value.get('objects') + if not pdfium_objects: + return (False, f'GCS entry "{deps_entry}" has no objects in PDFium DEPS.') + + chromium_path = 'src/' + deps_entry + chromium_dep_value = chromium_deps_parser.deps.get(chromium_path) + if not isinstance(chromium_dep_value, + dict) or (chromium_dep_value.get('dep_type') != 'gcs'): + return (False, f'GCS entry "{deps_entry}" not found in Chromium DEPS.') + + chromium_objects = chromium_dep_value.get('objects') + if not chromium_objects: + return (False, f'GCS entry "{deps_entry}" has no objects in Chromium DEPS.') + + if chromium_objects == pdfium_objects: + return (True, 'Revisions are the same.') + + return ( + True, + f'roll_downstream_gcs_deps.py --source-deps {chromium_deps_file} ' + f'--destination-deps {pdfium_deps_file} ' + f'--source-package {chromium_path} --destination-package {deps_entry}', + ) + + +def roll_dep_impl(chromium_deps_parser, pdfium_deps_parser, deps_entry, + chromium_deps_file, pdfium_deps_file): if deps_entry in UNSUPPORTED_ENTRIES: return (False, f'Rolling {deps_entry} is not supported.') + if deps_entry in pdfium_deps_parser.deps: + pdfium_dep_value = pdfium_deps_parser.deps.get(deps_entry) + if isinstance(pdfium_dep_value, dict) and (pdfium_dep_value.get('dep_type') + == 'gcs'): + return roll_gcs_dep_impl(chromium_deps_parser, pdfium_deps_parser, + deps_entry, chromium_deps_file, pdfium_deps_file) + # In PDFium DEPS, only search vars for the entry. if deps_entry not in pdfium_deps_parser.vars: return (False, f'Entry "{deps_entry}" not found in PDFium DEPS.') @@ -143,19 +182,23 @@ def roll_dep_impl(chromium_deps_parser, pdfium_deps_parser, deps_entry): '--ignore-dirty-tree --no-log') -def roll_dep(chromium_deps_content, pdfium_deps_content, deps_entry): +def roll_dep(chromium_deps_content, pdfium_deps_content, deps_entry, + chromium_deps_file, pdfium_deps_file): chromium_deps_parser = DepsParser(chromium_deps_content) pdfium_deps_parser = DepsParser(pdfium_deps_content) - return roll_dep_impl(chromium_deps_parser, pdfium_deps_parser, deps_entry) + return roll_dep_impl(chromium_deps_parser, pdfium_deps_parser, deps_entry, + chromium_deps_file, pdfium_deps_file) -def roll_all_deps(chromium_deps_content, pdfium_deps_content): +def roll_all_deps(chromium_deps_content, pdfium_deps_content, + chromium_deps_file, pdfium_deps_file): chromium_deps_parser = DepsParser(chromium_deps_content) pdfium_deps_parser = DepsParser(pdfium_deps_content) messages = [] for deps_entry in ALL_DEPS_ENTRIES: success, message = roll_dep_impl(chromium_deps_parser, pdfium_deps_parser, - deps_entry) + deps_entry, chromium_deps_file, + pdfium_deps_file) if not success: return (False, message) @@ -186,7 +229,9 @@ def main(): if args.deps_entry == 'ALL': success, maybe_messages = roll_all_deps(chromium_deps_content, - pdfium_deps_content) + pdfium_deps_content, + args.chromium_deps_file, + args.pdfium_deps_file) if not success: print(maybe_messages, file=sys.stderr) return 1 @@ -196,7 +241,8 @@ def main(): return 0 success, message = roll_dep(chromium_deps_content, pdfium_deps_content, - args.deps_entry) + args.deps_entry, args.chromium_deps_file, + args.pdfium_deps_file) if not success: print(message, file=sys.stderr) return 1 diff --git a/tools/roll_pdfium_deps_test.py b/tools/roll_pdfium_deps_test.py index c9bd7f4f84..7d0987eb71 100644 --- a/tools/roll_pdfium_deps_test.py +++ b/tools/roll_pdfium_deps_test.py @@ -784,6 +784,40 @@ }, ], }, + 'src/third_party/llvm-libclang': { + 'dep_type': 'gcs', + '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, + '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, + '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, + '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, + 'condition': 'host_os == "win"', + }, + ], + }, 'src/third_party/clang-format/script': Var('chromium_git') + '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' + @@ -2080,6 +2114,41 @@ Var('chromium_git') + '/external/github.com/llvm/llvm-project/libc.git@' + Var('llvm_libc_revision'), + 'third_party/llvm-libclang': { + 'dep_type': 'gcs', + '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, + '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, + '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, + '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, + 'condition': 'host_os == "win"', + }, + ], + }, + 'third_party/markupsafe': Var('chromium_git') + '/chromium/src/third_party/markupsafe.git@' + Var('markupsafe_revision'), @@ -2445,107 +2514,123 @@ ''' +def _run_roll_dep(deps_entry): + return roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, deps_entry, 'CHROMIUM_DEPS', + 'DEPS') + + class RollDepTest(unittest.TestCase): def testCipdDepsRolls(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'gn_version') + success, message = _run_roll_dep('gn_version') self.assertTrue(success) self.assertEqual( 'CIPD gn_version: ' 'git_revision:5964f499767097d81dbe034e8b541c3988168073', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'siso_version') + success, message = _run_roll_dep('siso_version') self.assertTrue(success) self.assertEqual( 'CIPD siso_version: ' 'git_revision:49dcca5d2be985d8ac6d512e59ee59e315264fb8', message) def testCipdDepsRollsNothingToRoll(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'android_ndk_version') + success, message = _run_roll_dep('android_ndk_version') + self.assertTrue(success) + self.assertEqual('Revisions are the same.', message) + + success, message = _run_roll_dep('cpython3_version') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'cpython3_version') + success, message = _run_roll_dep('ninja_version') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'ninja_version') + success, message = _run_roll_dep('reclient_version') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'reclient_version') + success, message = _run_roll_dep('result_adapter_revision') + self.assertTrue(success) + self.assertEqual('Revisions are the same.', message) + + def testGcsDepsRolls(self): + success, message = _run_roll_dep('third_party/llvm-build/Release+Asserts') + self.assertTrue(success) + self.assertEqual( + 'roll_downstream_gcs_deps.py ' + '--source-deps CHROMIUM_DEPS --destination-deps DEPS ' + '--source-package src/third_party/llvm-build/Release+Asserts ' + '--destination-package third_party/llvm-build/Release+Asserts', message) + + def testGcsDepsRollsNothingToRoll(self): + success, message = _run_roll_dep('third_party/llvm-libclang') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'result_adapter_revision') + success, message = _run_roll_dep('third_party/rust-toolchain') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) def testGitDepsRollsToChromiumRevision(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'gtest_revision') + success, message = _run_roll_dep('gtest_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/googletest/src ' '--roll-to 4fe3307fb2d9f86d19777c7eb0e4809e9694dde7 ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'icu_revision') + success, message = _run_roll_dep('icu_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/icu ' '--roll-to a86a32e67b8d1384b33f8fa48c83a6079b86f8cd ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'jpeg_turbo_revision') + success, message = _run_roll_dep('jpeg_turbo_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/libjpeg_turbo ' '--roll-to 6383cf609c1f63c18af0f59b2738caa0c6c7e379 ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'libcxx_revision') + success, message = _run_roll_dep('libcxx_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/libc++/src ' '--roll-to c5dd8ade977af3a7441bbf99a2dcac2d5820e702 ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'libcxxabi_revision') + success, message = _run_roll_dep('libcxxabi_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/libc++abi/src ' '--roll-to 83a852080747b9a362e8f9e361366b7a601f302c ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'libunwind_revision') + success, message = _run_roll_dep('libunwind_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/libunwind/src ' '--roll-to 88fc07ed143a5b3bbf45d430b72a4617ee9e235f ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'llvm_libc_revision') + success, message = _run_roll_dep('llvm_libc_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/llvm-libc/src ' '--roll-to 03f822d2a88c8f68f6a92c5cb3e79ccc3002e8a9 ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'nasm_source_revision') + success, message = _run_roll_dep('nasm_source_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/nasm ' '--roll-to af5eeeb054bebadfbb79c7bcd100a95e2ad4525f ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'v8_revision') + success, message = _run_roll_dep('v8_revision') self.assertTrue(success) self.assertEqual( 'roll-dep v8 ' @@ -2553,7 +2638,7 @@ def testGitDepsRollsToChromiumRevision(self): '--ignore-dirty-tree --no-log', message) def testGitDepsRollsToChromiumRevisionForFreeType(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'freetype_revision') + success, message = _run_roll_dep('freetype_revision') self.assertTrue(success) self.assertEqual( 'third_party/freetype/roll-freetype.sh ' @@ -2561,104 +2646,93 @@ def testGitDepsRollsToChromiumRevisionForFreeType(self): '--ignore-dirty-tree --no-log', message) def testGitDepsRollsToChromiumRevisionNothingToRoll(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'clang_format_revision') + success, message = _run_roll_dep('clang_format_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'cpu_features_revision') + success, message = _run_roll_dep('cpu_features_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'dragonbox_revision') + success, message = _run_roll_dep('dragonbox_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'fast_float_revision') + success, message = _run_roll_dep('fast_float_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'fp16_revision') + success, message = _run_roll_dep('fp16_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'highway_revision') + success, message = _run_roll_dep('highway_revision') self.assertTrue(success) self.assertEqual('Revisions are the same.', message) def testGitDepsRollsToTipOfTree(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'abseil_revision') + success, message = _run_roll_dep('abseil_revision') self.assertTrue(success) self.assertEqual( 'roll-dep third_party/abseil-cpp ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'brotli_revision') + success, message = _run_roll_dep('brotli_revision') self.assertTrue(success) self.assertEqual('roll-dep third_party/brotli --ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'build_revision') + success, message = _run_roll_dep('build_revision') self.assertTrue(success) self.assertEqual('roll-dep build --ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'buildtools_revision') + success, message = _run_roll_dep('buildtools_revision') self.assertTrue(success) self.assertEqual('roll-dep buildtools --ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'clang_revision') + success, message = _run_roll_dep('clang_revision') self.assertTrue(success) self.assertEqual('roll-dep tools/clang --ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'partition_allocator_revision') + success, message = _run_roll_dep('partition_allocator_revision') self.assertTrue(success) self.assertEqual( 'roll-dep base/allocator/partition_allocator ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'rust_revision') + success, message = _run_roll_dep('rust_revision') self.assertTrue(success) self.assertEqual('roll-dep third_party/rust --ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'testing_rust_revision') + success, message = _run_roll_dep('testing_rust_revision') self.assertTrue(success) self.assertEqual( 'roll-dep testing/scripts/rust ' '--ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'tools_rust_revision') + success, message = _run_roll_dep('tools_rust_revision') self.assertTrue(success) self.assertEqual('roll-dep tools/rust --ignore-dirty-tree --no-log', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'tools_win_revision') + success, message = _run_roll_dep('tools_win_revision') self.assertTrue(success) self.assertEqual('roll-dep tools/win --ignore-dirty-tree --no-log', message) def testInvalidEntry(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, 'invalid') + success, message = _run_roll_dep('invalid') self.assertFalse(success) self.assertEqual('Entry "invalid" not found in PDFium DEPS.', message) def testUnsupportedEntry(self): - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'pdfium_tests_revision') + success, message = _run_roll_dep('pdfium_tests_revision') self.assertFalse(success) self.assertEqual('Rolling pdfium_tests_revision is not supported.', message) - success, message = roll_dep(CHROMIUM_DEPS, PDFIUM_DEPS, - 'test_fonts_revision') + success, message = _run_roll_dep('test_fonts_revision') self.assertFalse(success) self.assertEqual('Rolling test_fonts_revision is not supported.', message) @@ -2713,13 +2787,18 @@ def testRollAll(self): '--roll-to 83b0b9e6065a60617504aa1d6e6997c511c3afac ' '--ignore-dirty-tree --no-log', 'roll-dep testing/scripts/rust --ignore-dirty-tree --no-log', + 'roll_downstream_gcs_deps.py ' + '--source-deps CHROMIUM_DEPS --destination-deps DEPS ' + '--source-package src/third_party/llvm-build/Release+Asserts ' + '--destination-package third_party/llvm-build/Release+Asserts', 'roll-dep tools/rust --ignore-dirty-tree --no-log', 'roll-dep tools/win --ignore-dirty-tree --no-log', 'roll-dep v8 --roll-to 5bd40de12569247beb113659504906aa41e48959 ' '--ignore-dirty-tree --no-log', ] - success, message = roll_all_deps(CHROMIUM_DEPS, PDFIUM_DEPS) + success, message = roll_all_deps(CHROMIUM_DEPS, PDFIUM_DEPS, + 'CHROMIUM_DEPS', 'DEPS') self.assertTrue(success) self.assertEqual(EXPECTED_ALL, message) diff --git a/xfa/fde/BUILD.gn b/xfa/fde/BUILD.gn index 31d6e55fb1..c524733003 100644 --- a/xfa/fde/BUILD.gn +++ b/xfa/fde/BUILD.gn @@ -22,12 +22,12 @@ source_set("fde") { "../../:pdfium_noshorten_config", "../:xfa_warnings", ] - deps = [ + public_deps = [ "../../core/fxcrt", "../../core/fxge", - "../fgas/font", "../fgas/layout", ] + deps = [ "../fgas/font" ] visibility = [ "../../*" ] } @@ -40,6 +40,8 @@ pdfium_unittest_source_set("unittests") { ":fde", "../../core/fdrm", "../../core/fxge", + "../../testing:test_environments", + "../../testing:test_support", "../fgas/font", ] pdfium_root_dir = "../../" diff --git a/xfa/fde/cfde_textout_unittest.cpp b/xfa/fde/cfde_textout_unittest.cpp index ce5a9b9ce6..54980b78f5 100644 --- a/xfa/fde/cfde_textout_unittest.cpp +++ b/xfa/fde/cfde_textout_unittest.cpp @@ -93,14 +93,20 @@ class CFDETextOutTest : public testing::Test { TEST_F(CFDETextOutTest, DrawLogicTextBasic) { text_out().DrawLogicText(device(), L"foo", CFX_RectF(0, 0, 2100, 100)); const char* checksum = []() { -#if defined(PDF_USE_SKIA) && BUILDFLAG(IS_WIN) +#if defined(PDF_USE_SKIA) if (CFX_GEModule::IsSkiaRenderer()) { +#if BUILDFLAG(IS_WIN) return "02c55ce12d3c31ae32fa08fe59c38996"; +#elif defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + return "8a6b320f8a4ae1febfc6e50513506dcf"; + } +#endif } #endif #if defined(PDF_ENABLE_FONTATIONS) if (CFX_GEModule::IsFontations()) { - return "59f4a2cfb7938032f144954642babe58"; + return "0064590d0532663140d521a2ea6780b1"; } #endif return "c143f8450f661a489cc9423de7cc1acc"; @@ -138,12 +144,17 @@ class CFDETextOutLargeBitmapTest : public CFDETextOutTest { const char* GetLargeTextBlobChecksum() { #if defined(PDF_USE_SKIA) if (CFX_GEModule::IsSkiaRenderer()) { +#if defined(PDF_ENABLE_FONTATIONS) + if (CFX_GEModule::IsFontations()) { + return "8b657167ef063bca5adb38f6a5d5ae54"; + } +#endif return "e9aaffff1ea680bd5dc40a7b8904788d"; } #endif #if defined(PDF_ENABLE_FONTATIONS) if (CFX_GEModule::IsFontations()) { - return "89adff3e02833425d42a2f89ef8fd7e3"; + return "78842f43dce9b89af92f17b56d9ceaab"; } #endif return "add7cf2819b3e1397d8a60a9ec436a86"; diff --git a/xfa/fgas/crt/BUILD.gn b/xfa/fgas/crt/BUILD.gn index 6eb8526bfc..e37b658820 100644 --- a/xfa/fgas/crt/BUILD.gn +++ b/xfa/fgas/crt/BUILD.gn @@ -21,10 +21,8 @@ source_set("crt") { "../../../:pdfium_noshorten_config", "../../:xfa_warnings", ] - deps = [ - "../../../core/fpdfapi/page", - "../../../core/fxcrt", - ] + public_deps = [ "../../../core/fxcrt" ] + deps = [ "../../../core/fpdfapi/page" ] visibility = [ "../../../*" ] } @@ -37,7 +35,9 @@ pdfium_unittest_source_set("unittests") { ":crt", "../../../core/fpdfapi/page", "../../../fxjs:gc", + "../../../testing:test_support", "../../fxfa/parser", + "//v8:cppgc_headers", ] pdfium_root_dir = "../../../" } diff --git a/xfa/fgas/font/BUILD.gn b/xfa/fgas/font/BUILD.gn index f4f8ded320..aa11572c94 100644 --- a/xfa/fgas/font/BUILD.gn +++ b/xfa/fgas/font/BUILD.gn @@ -27,13 +27,15 @@ source_set("font") { "../../../:pdfium_noshorten_config", "../../:xfa_warnings", ] + public_deps = [ + "../../../core/fxcrt", + "../../../core/fxge", + ] deps = [ "../../../constants", "../../../core/fpdfapi/font", "../../../core/fpdfapi/page", "../../../core/fpdfapi/parser", - "../../../core/fxcrt", - "../../../core/fxge", ] visibility = [ "../../../*" ] } diff --git a/xfa/fgas/graphics/BUILD.gn b/xfa/fgas/graphics/BUILD.gn index a1408c41f3..87d0357a6d 100644 --- a/xfa/fgas/graphics/BUILD.gn +++ b/xfa/fgas/graphics/BUILD.gn @@ -24,9 +24,10 @@ source_set("graphics") { "../../../:pdfium_noshorten_config", "../../:xfa_warnings", ] - deps = [ + public_deps = [ "../../../core/fxcrt", "../../../core/fxge", ] + visibility = [ "../../../*" ] } diff --git a/xfa/fgas/layout/BUILD.gn b/xfa/fgas/layout/BUILD.gn index 90495c48ea..79724c12aa 100644 --- a/xfa/fgas/layout/BUILD.gn +++ b/xfa/fgas/layout/BUILD.gn @@ -31,8 +31,8 @@ source_set("layout") { "fgas_arabic.h", "fgas_linebreak.h", ] + public_deps = [ "../../../core/fxcrt" ] deps = [ - "../../../core/fxcrt", "../../../core/fxcrt/css", "../../../core/fxge", "../font", diff --git a/xfa/fwl/BUILD.gn b/xfa/fwl/BUILD.gn index 15a0d2f6cd..fef87a5d7f 100644 --- a/xfa/fwl/BUILD.gn +++ b/xfa/fwl/BUILD.gn @@ -112,15 +112,16 @@ source_set("fwl") { "../../:pdfium_noshorten_config", "../:xfa_warnings", ] - deps = [ + public_deps = [ "../../core/fxcrt", "../../core/fxge", "../../fxbarcode", "../../fxjs:gc", "../fde", - "../fgas/font", "../fgas/graphics", + "//v8:cppgc_headers", ] + deps = [ "../fgas/font" ] visibility = [ "../../*" ] } diff --git a/xfa/fxfa/BUILD.gn b/xfa/fxfa/BUILD.gn index 6a862f649a..7cb4729d5e 100644 --- a/xfa/fxfa/BUILD.gn +++ b/xfa/fxfa/BUILD.gn @@ -84,24 +84,27 @@ source_set("fxfa") { "fxfa.h", "fxfa_basic.h", ] - deps = [ - "../../constants", - "../../core/fpdfapi/parser", + public_deps = [ "../../core/fpdfdoc", "../../core/fxcodec", "../../core/fxcrt", "../../core/fxcrt/css", "../../core/fxge", "../../fxbarcode", - "../../fxjs", "../../fxjs:gc", - "../fde", - "../fgas/font", - "../fgas/graphics", "../fgas/layout", "../fwl", "layout", "parser", + "//v8:cppgc_headers", + ] + deps = [ + "../../constants", + "../../core/fpdfapi/parser", + "../../fxjs", + "../fde", + "../fgas/font", + "../fgas/graphics", ] allow_circular_includes_from = [ "../../fxjs", @@ -125,6 +128,7 @@ pdfium_unittest_source_set("unittests") { deps = [ ":fxfa", "../../fxjs:gc", + "//v8:cppgc_headers", ] pdfium_root_dir = "../../" } diff --git a/xfa/fxfa/cxfa_eventparam.cpp b/xfa/fxfa/cxfa_eventparam.cpp index 1edd1c71e4..334ad21bd8 100644 --- a/xfa/fxfa/cxfa_eventparam.cpp +++ b/xfa/fxfa/cxfa_eventparam.cpp @@ -19,5 +19,7 @@ CXFA_EventParam& CXFA_EventParam::operator=(CXFA_EventParam&& other) noexcept = default; WideString CXFA_EventParam::GetNewText() const { - return prev_text_.First(sel_start_) + change_ + prev_text_.Substr(sel_end_); + return WideString({prev_text_.AsStringView().First(sel_start_), + change_.AsStringView(), + prev_text_.AsStringView().Substr(sel_end_)}); } diff --git a/xfa/fxfa/cxfa_fffield.cpp b/xfa/fxfa/cxfa_fffield.cpp index 4f8dc3e844..3b51d360bf 100644 --- a/xfa/fxfa/cxfa_fffield.cpp +++ b/xfa/fxfa/cxfa_fffield.cpp @@ -146,6 +146,10 @@ bool CXFA_FFField::LoadWidget() { } void CXFA_FFField::SetEditScrollOffset() { + if (!GetNormalWidget()) { + return; + } + XFA_FFWidgetType eType = node_->GetFFWidgetType(); if (eType != XFA_FFWidgetType::kTextEdit && eType != XFA_FFWidgetType::kNumericEdit && diff --git a/xfa/fxfa/cxfa_fftextedit.cpp b/xfa/fxfa/cxfa_fftextedit.cpp index e2451a092c..843e5196fb 100644 --- a/xfa/fxfa/cxfa_fftextedit.cpp +++ b/xfa/fxfa/cxfa_fftextedit.cpp @@ -226,10 +226,11 @@ void CXFA_FFTextEdit::ValidateNumberField(const WideString& wsText) { } WideString wsSomField = GetNode()->GetSOMExpression(); - pAppProvider->MsgBox( - wsText + WideString::FromASCII(" can not contain ") + wsSomField, - pAppProvider->GetAppTitle(), static_cast(AlertIcon::kError), - static_cast(AlertButton::kOK)); + pAppProvider->MsgBox(WideString({wsText.AsStringView(), L" can not contain ", + wsSomField.AsStringView()}), + pAppProvider->GetAppTitle(), + static_cast(AlertIcon::kError), + static_cast(AlertButton::kOK)); } bool CXFA_FFTextEdit::IsDataChanged() { diff --git a/xfa/fxfa/formcalc/BUILD.gn b/xfa/fxfa/formcalc/BUILD.gn index 114c730199..612a09e66f 100644 --- a/xfa/fxfa/formcalc/BUILD.gn +++ b/xfa/fxfa/formcalc/BUILD.gn @@ -23,10 +23,12 @@ source_set("formcalc") { "../../../:pdfium_noshorten_config", "../../:xfa_warnings", ] - deps = [ + public_deps = [ "../../../core/fxcrt", "../../../fxjs:gc", + "//v8:cppgc_headers", ] + visibility = [ "../../../*" ] } @@ -39,6 +41,7 @@ pdfium_unittest_source_set("unittests") { deps = [ ":formcalc", "../../../fxjs:gc", + "//v8:cppgc_headers", ] pdfium_root_dir = "../../../" } diff --git a/xfa/fxfa/layout/BUILD.gn b/xfa/fxfa/layout/BUILD.gn index 1d28f04acc..1c517ded73 100644 --- a/xfa/fxfa/layout/BUILD.gn +++ b/xfa/fxfa/layout/BUILD.gn @@ -29,12 +29,13 @@ source_set("layout") { "../../../:pdfium_noshorten_config", "../../:xfa_warnings", ] - deps = [ + public_deps = [ "../../../core/fxcrt", - "../../../fxjs", "../../../fxjs:gc", "../parser", + "//v8:cppgc_headers", ] + deps = [ "../../../fxjs" ] visibility = [ "../../../*" ] } diff --git a/xfa/fxfa/parser/BUILD.gn b/xfa/fxfa/parser/BUILD.gn index f42bd066f9..dee7872ed5 100644 --- a/xfa/fxfa/parser/BUILD.gn +++ b/xfa/fxfa/parser/BUILD.gn @@ -682,16 +682,19 @@ source_set("parser") { "../../../:pdfium_noshorten_config", "../../:xfa_warnings", ] - deps = [ - "../../../core/fxcodec", + public_deps = [ "../../../core/fxcrt", "../../../core/fxge", "../../../fxjs", "../../../fxjs:gc", - "../../fde", "../../fgas/crt", - "../../fgas/font", "../../fgas/graphics", + "//v8:cppgc_headers", + ] + deps = [ + "../../../core/fxcodec", + "../../fde", + "../../fgas/font", ] visibility = [ "../../../*" ] } @@ -713,6 +716,8 @@ pdfium_unittest_source_set("unittests") { ":parser", "../../../fxjs", "../../../fxjs:gc", + "../../../testing:test_support", + "//v8:cppgc_headers", ] pdfium_root_dir = "../../../" } diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp index cff2ebbc97..f7e2475138 100644 --- a/xfa/fxfa/parser/cxfa_node.cpp +++ b/xfa/fxfa/parser/cxfa_node.cpp @@ -1595,10 +1595,9 @@ WideString CXFA_Node::GetNameExpression() { WideString wsName = GetNameExpressionSinglePath(this); CXFA_Node* parent = GetParent(); while (parent) { - WideString wsParent = GetNameExpressionSinglePath(parent); - wsParent += L"."; - wsParent += wsName; - wsName = std::move(wsParent); + WideString parent_path = GetNameExpressionSinglePath(parent); + wsName = + WideString({parent_path.AsStringView(), L'.', wsName.AsStringView()}); parent = parent->GetParent(); } return wsName; @@ -2794,9 +2793,10 @@ XFA_EventError CXFA_Node::ProcessNullTestValidate(CXFA_FFDocView* pDocView, if (wsNullMsg.IsEmpty()) { wsCaptionName = GetValidateCaptionName(bVersionFlag); - wsNullMsg = wsCaptionName + - L" cannot be blank. To ignore validations for " + - wsCaptionName + L", click Ignore."; + wsNullMsg = + WideString({wsCaptionName.AsStringView(), + L" cannot be blank. To ignore validations for ", + wsCaptionName.AsStringView(), L", click Ignore."}); } if (pAppProvider->MsgBox(wsNullMsg, wsTitle, static_cast(AlertIcon::kWarning), @@ -2884,15 +2884,16 @@ WideString CXFA_Node::GetValidateCaptionName(bool bVersionFlag) { WideString CXFA_Node::GetValidateMessage(bool bError, bool bVersionFlag) { WideString wsCaptionName = GetValidateCaptionName(bVersionFlag); if (bVersionFlag) { - return wsCaptionName + L" validation failed"; + return WideString({wsCaptionName.AsStringView(), L" validation failed"}); } - WideString result = - L"The value you entered for " + wsCaptionName + L" is invalid."; if (!bError) { - result += - L" To ignore validations for " + wsCaptionName + L", click Ignore."; + return WideString({L"The value you entered for ", + wsCaptionName.AsStringView(), + L" is invalid. To ignore validations for ", + wsCaptionName.AsStringView(), L", click Ignore."}); } - return result; + return WideString({L"The value you entered for ", + wsCaptionName.AsStringView(), L" is invalid."}); } XFA_EventError CXFA_Node::ExecuteScript(CXFA_FFDocView* pDocView,