Snap for 10878163 from c9a8e744a91219fda0827d81ff650e9940ad3a24 to 24Q1-release

Change-Id: Ica7aaea651ce0f197668f56eab3701f1fe57585a
diff --git a/.gn b/.gn
index b2bf99d..e39d6f7 100644
--- a/.gn
+++ b/.gn
@@ -11,6 +11,9 @@
 script_executable = "python3"
 
 default_args = {
+  # Required for the Fuchsia SDK
+  fuchsia_target_api_level = 11
+
   # PDFs only need to run JavaScript.
   v8_enable_webassembly = false
 
diff --git a/.vpython3 b/.vpython3
index b6cb1b5..52840c6 100644
--- a/.vpython3
+++ b/.vpython3
@@ -45,8 +45,8 @@
   version: "version:2.10"
 >
 wheel: <
-  name: "infra/python/wheels/requests-py2_py3"
-  version: "version:2.26.0"
+  name: "infra/python/wheels/requests-py3"
+  version: "version:2.31.0"
 >
 wheel: <
   name: "infra/python/wheels/urllib3-py2_py3"
diff --git a/AUTHORS b/AUTHORS
index f8e3ed9..5b98287 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -17,10 +17,12 @@
 Chery Cherian <cherycherian@gmail.com>
 Claudio DeSouza <claudiomdsjr@gmail.com>
 Dan Ilan <danilan@gmail.com>
+Dorian Rudolph <dorianrudo97@gmail.com>
 Felix Kauselmann <licorn@gmail.com>
 GiWan Go <gogil@stealien.com>
 Huy Ngo <huyna89@gmail.com>
 Jiang Jiang <jiangj@opera.com>
+Justin Pierce <brkfstmnchr@gmail.com>
 Ke Liu <stackexploit@gmail.com>
 Luật Nguyễn <manhluat93.php@gmail.com>
 Manuel Geißer <geisserml@gmail.com>
@@ -44,6 +46,7 @@
 Dropbox <*@dropbox.com>
 Foxit Software Inc <*@foxitsoftware.com>
 Google Inc. <*@google.com>
+Igalia S.L. <*@igalia.com>
 LG Electronics, Inc. <*@lge.com>
 Loongson Technology Corporation Limited. <*@loongson.cn>
 Microsoft <*@microsoft.com>
diff --git a/BUILD.gn b/BUILD.gn
index cb1da47..07a5b93 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -9,7 +9,10 @@
 
 group("default") {
   testonly = true
-  deps = [ ":pdfium_all" ]
+  deps = [ ":pdfium" ]
+  if (pdf_is_standalone) {
+    deps += [ ":pdfium_all" ]
+  }
 }
 
 group("freetype_common") {
@@ -164,8 +167,7 @@
                     # signed/unsigned mismatch
 
         "/wd4267",  # 'var' : conversion from 'size_t' to 'type', possible loss
-                    # of
-                    # data
+                    # of data
 
         "/wd4305",  # 'identifier' : truncation from 'type1' to 'type2'
         "/wd4389",  # 'operator' : signed/unsigned mismatch
@@ -197,14 +199,6 @@
       ]
 
       if (current_cpu == "x86") {
-        cflags += [
-          # VC++ 2015 changes 32-bit size_t truncation warnings from 4244 to
-          # 4267. Example: short TruncTest(size_t x) { return x; }
-          # Since we disable 4244 we need to disable 4267 during migration.
-          # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-          "/wd4267",
-        ]
-
         if (msvc_use_sse2) {
           cflags += [ "/arch:SSE2" ]
         }
@@ -221,16 +215,6 @@
 
     # May flag some issues when converting int to size_t.
     cflags += [ "-Wtautological-unsigned-zero-compare" ]
-
-    # Catch misuse of cppgc in XFA.
-    if (pdf_enable_xfa && clang_use_chrome_plugins) {
-      cflags += [
-        "-Xclang",
-        "-add-plugin",
-        "-Xclang",
-        "blink-gc-plugin",
-      ]
-    }
   }
 
   if (!is_win && !is_clang) {
@@ -288,11 +272,59 @@
       "/wd4577",
     ]
   }
+  if (is_clang) {
+    cflags += [ "-Wcovered-switch-default" ]
+  }
+}
+
+config("pdfium_plugin_config") {
+  cflags = []
+  defines = []
+  if (clang_use_chrome_plugins) {
+    # Catch misuse of C-style pointers.
+    # TODO(tsepez): enable for windows, too.
+    if (!is_win) {
+      cflags += [
+        "-Xclang",
+        "-plugin-arg-find-bad-constructs",
+        "-Xclang",
+        "check-raw-ptr-fields",
+
+        "-Xclang",
+        "-plugin-arg-find-bad-constructs",
+        "-Xclang",
+        "raw-ptr-exclude-path=public",
+
+        "-Xclang",
+        "-plugin-arg-find-bad-constructs",
+        "-Xclang",
+        "raw-ptr-exclude-path=test",
+
+        # TODO(tsepez): enforce raw_ref<> as well.
+        # "-Xclang",
+        # "-plugin-arg-find-bad-constructs",
+        # "-Xclang",
+        # "check-raw-ref-fields",
+      ]
+      defines += [ "PDF_ENABLE_UNOWNED_PTR_EXCLUSION" ]
+    }
+
+    # Catch misuse of cppgc in XFA.
+    if (pdf_enable_xfa) {
+      cflags += [
+        "-Xclang",
+        "-add-plugin",
+        "-Xclang",
+        "blink-gc-plugin",
+      ]
+    }
+  }
 }
 
 config("pdfium_strict_config") {
   configs = [
     ":pdfium_core_config",
+    ":pdfium_plugin_config",
     "//build/config/compiler:wexit_time_destructors",
     "//build/config/compiler:wglobal_constructors",
   ]
@@ -359,7 +391,6 @@
     "fpdfsdk/formfiller",
     "fxjs",
     "third_party:pdfium_base",
-    "third_party:skia_shared",
   ]
 
   public_deps = [
@@ -439,18 +470,15 @@
     "//testing/gtest",
   ]
   configs += [ ":pdfium_core_config" ]
-
   if (is_android) {
     use_raw_android_executable = true
   }
-
   if (pdf_enable_v8) {
     configs += [ "//v8:external_startup_data" ]
     deps += [
       "fxjs:unittests",
       "//v8",
     ]
-
     if (pdf_enable_xfa) {
       deps += [
         "core/fxcrt/css:unittests",
@@ -530,6 +558,7 @@
 }
 
 executable("pdfium_diff") {
+  visibility += [ "testing/tools:test_runner_py" ]
   testonly = true
   sources = [ "testing/image_diff/image_diff.cpp" ]
   deps = [
@@ -541,30 +570,18 @@
   configs += [ ":pdfium_strict_config" ]
 }
 
-if (pdf_is_standalone) {
-  source_set("samples") {
-    testonly = true
-    deps = [ "//samples" ]
-  }
-
-  group("fuzzers") {
-    testonly = true
-    deps = [ "//testing/fuzzers" ]
-  }
-}
-
 group("pdfium_all") {
   testonly = true
   deps = [
     ":pdfium_diff",
     ":pdfium_embeddertests",
     ":pdfium_unittests",
+    "samples",
+    "testing/fuzzers",
   ]
+
   if (pdf_is_standalone) {
-    deps += [
-      ":fuzzers",
-      ":samples",
-    ]
+    deps += [ "testing/tools:test_runner_py" ]
   }
 }
 
diff --git a/DEPS b/DEPS
index e5aa4d8..5a55c16 100644
--- a/DEPS
+++ b/DEPS
@@ -25,7 +25,19 @@
   'checkout_v8': 'checkout_configuration != "minimal"',
 
   # By default, download the fuchsia sdk from the public sdk directory.
-  'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/gn/',
+  'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/core/',
+
+  # Fetch configuration files required for the 'use_remoteexec' gn arg
+  'download_remoteexec_cfg': False,
+  # RBE instance to use for running remote builds
+  'rbe_instance': Str('projects/rbe-chrome-untrusted/instances/default_instance'),
+  # RBE project to download rewrapper config files for. Only needed if
+  # different from the project used in 'rbe_instance'
+  'rewrapper_cfg_project': Str(''),
+  # reclient CIPD package
+  'reclient_package': 'infra/rbe/client/',
+  # reclient CIPD package version
+  'reclient_version': 're_client_version:0.109.0.927890d-gomaip',
 
   'chromium_git': 'https://chromium.googlesource.com',
   'pdfium_git': 'https://pdfium.googlesource.com',
@@ -34,51 +46,59 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling abseil
   # and whatever else without interference from each other.
-  'abseil_revision': '20c8ae002db022653b94e80dec69306558818ebf',
+  'abseil_revision': '2288062eef9624e8b48070ed2447139d3fd4a1c5',
   # Three lines of non-changing comments so that
-  # the commit queue can handle CLs rolling android_ndk
+  # the commit queue can handle CLs rolling android_toolchain
   # and whatever else without interference from each other.
-  'android_ndk_revision': '8388a2be5421311dc75c5f937aae13d821a27f3d',
+  'android_toolchain_version': 'R_8suM8m0oHbZ1awdxGXvKEFpAOETscbfZxkkMthyk8C',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build
   # and whatever else without interference from each other.
-  'build_revision': 'c44ccbfc028a136e7d39bf79e45a92a91d7b5beb',
+  'build_revision': '336e34d44c88d1160d150bd6c8d93e203a424b42',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling buildtools
   # and whatever else without interference from each other.
-  'buildtools_revision': '3ee69a5c6bc8d115dea09bbf8e536f529e7e12a8',
+  'buildtools_revision': '16be42a9ff1f7e4a3e53b93b3adc181fa7ff9161',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ac30cc4bc7d30e574625e1f6a77fba5df0719ed6',
+  'catapult_revision': '220cbb13b5a7485be09813e7da4123088419a76a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling clang format
   # and whatever else without interference from each other.
-  'clang_format_revision': 'f97059df7f8b205064625cdb5f97b56668a125ef',
+  'clang_format_revision': 'e5337933f2951cacd3aeacd238ce4578163ca0b9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling clang
   # and whatever else without interference from each other.
-  'clang_revision': 'f6862472607fa628642713b615a9e119d51bd43d',
+  'clang_revision': '236e66ffd61f3bd710d20d20a67e71a1f1f55cba',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling code_coverage
   # and whatever else without interference from each other.
-  'code_coverage_revision': '9b51524c4f7575ee90d9173507e6f13f814d7001',
+  'code_coverage_revision': 'bce4cdc2309e9a7f1e0ff1d9310e0d0302aa67e0',
+  # 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.
+  'cpu_features_revision': '936b9ab5515dead115606559502e3864958f7f6e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling depot_tools
   # and whatever else without interference from each other.
-  'depot_tools_revision': 'ab117384e6c0384ba5aab66c13d8f1008d964655',
+  'depot_tools_revision': '59e10115417ac77f47e42c3f13a4f6b58ebe70c2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'b0a4f99278aa7e14bd1d0d9e40ad28dce543fde6',
+  'freetype_revision': 'b2584c738f1a92e6369890cff0504cc044315b38',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling freetype
+  # and whatever else without interference from each other.
+  'fuchsia_gn_sdk_revision': '0d6902558d92fe3d49ba9a8f638ddea829be595b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:12.20230330.3.1',
+  'fuchsia_version': 'version:14.20230727.2.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling GN CIPD package version
   # and whatever else without interference from each other.
-  'gn_version': 'git_revision:41fef642de70ecdcaaa26be96d56a0398f95abd4',
+  'gn_version': 'git_revision:3fccef9033b950e8935e8debeba9fbd71617bc74',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling gtest
   # and whatever else without interference from each other.
@@ -86,29 +106,29 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling icu
   # and whatever else without interference from each other.
-  'icu_revision': '1e49ac26ddc712b1ab702f69023cbc57e9ae6628',
+  'icu_revision': 'de4ce0071eb47ed54cbda54869001210cf3a8ae5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling instrumented_lib
   # and whatever else without interference from each other.
-  'instrumented_lib_revision': '0f536d22dbed454b1254c7e6d7130eab28fba1fa',
+  'instrumented_lib_revision': '032e9c850ab975f7c088a625dcf2256917dbdfa6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling jinja2
   # and whatever else without interference from each other.
-  'jinja2_revision': '264c07d7e64f2874434a3b8039e101ddf1b01e7e',
+  'jinja2_revision': '515dd10de9bf63040045902a4a310d2ba25213a0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling jpeg_turbo
   # and whatever else without interference from each other.
-  'jpeg_turbo_revision': 'aa4075f116e4312537d0d3e9dbd5e31096539f94',
+  'jpeg_turbo_revision': '30bdb85e302ecfc52593636b2f44af438e05e784',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libc++
   # and whatever else without interference from each other.
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision': 'ab37483b426c16ce33f8f0064be571513d5a8c34',
+  'libcxx_revision': '84fb809dd6dae36d556dc0bb702c6cc2ce9d4b80',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libc++abi
   # and whatever else without interference from each other.
-  'libcxxabi_revision': '4a9d0560b481a96821bec591325b50a5063f4a32',
+  'libcxxabi_revision': 'd4760c0af99ccc9bce077960d5ddde4d66146c05',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libpng
   # and whatever else without interference from each other.
@@ -116,15 +136,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libunwind
   # and whatever else without interference from each other.
-  'libunwind_revision': 'f3464caa6aa83a5eb924a5ae3779e974bd1bebf4',
-  # Three lines of non-changing comments so that
-  # the commit queue can handle CLs rolling lss
-  # and whatever else without interference from each other.
-  'lss_revision': 'ce877209e11aa69dcfffbd53ef90ea1d07136521',
+  'libunwind_revision': 'e5a9c50e5e0b620a8886df1c4677b12404620fb6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling markupsafe
   # and whatever else without interference from each other.
-  'markupsafe_revision': '13f4e8c9e206567eeb13bf585406ddc574005748',
+  'markupsafe_revision': '006709ba3ed87660a17bd4548c45663628f5ed85',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nasm_source
   # and whatever else without interference from each other.
@@ -136,11 +152,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling partition_allocator
   # and whatever else without interference from each other.
-  'partition_allocator_revision': '118936d6793d0c259bd9023717d37367a7b04320',
+  'partition_allocator_revision': 'f91d5ba232cbe61b9740b4101a5b5bea3cd631ed',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling pdfium_tests
   # and whatever else without interference from each other.
-  'pdfium_tests_revision': 'd1386521f5d606b9110143aa40b23651b17aded2',
+  'pdfium_tests_revision': 'dc2cd9afdd1bad8666072416c340ad1c6a01e388',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling resultdb
   # and whatever else without interference from each other.
@@ -148,7 +164,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling skia
   # and whatever else without interference from each other.
-  'skia_revision': 'ad459a5b8df474d881209696b3fb4f038e0d3a55',
+  'skia_revision': '6119b059f50a1858f882068cc6738571240b4f5d',
   # 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.
@@ -156,7 +172,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling tools_memory
   # and whatever else without interference from each other.
-  'tools_memory_revision': '13f0b81ce581364c5f0f2e9e16d6120073dc56a6',
+  'tools_memory_revision': '2a4c4ba1f4a94231b01280a0c63d3fe4404cc9c2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling trace_event
   # and whatever else without interference from each other.
@@ -164,11 +180,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling v8
   # and whatever else without interference from each other.
-  'v8_revision': 'f66b0bc93b1edc09a6ff66c2e0ae0f2848d90be7',
+  'v8_revision': '41ff48bd620584b425618e7f8b51617c46c4d67f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling zlib
   # and whatever else without interference from each other.
-  'zlib_revision': 'b890619bc2b193b8fbe9c1c053f4cd19a9791d92',
+  'zlib_revision': '526382e41c9c5275dc329db4328b54e4f344a204',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -224,6 +240,18 @@
     'condition': 'host_os == "mac"',
   },
 
+  'buildtools/reclient': {
+    'packages': [
+      {
+        'package': Var('reclient_package') + '${{platform}}',
+        'version': Var('reclient_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+  },
+
+  # TODO(chromium:1458042): Remove these paths, when chromium builds files
+  # have moved to third_party/lib*/src paths.
   'buildtools/third_party/libc++/trunk':
     Var('chromium_git') +
         '/external/github.com/llvm/llvm-project/libcxx.git@' +
@@ -259,10 +287,15 @@
     Var('chromium_git') + '/chromium/src/third_party/abseil-cpp.git@' +
         Var('abseil_revision'),
 
-  'third_party/android_ndk': {
-    'url': Var('chromium_git') + '/android_ndk.git@' +
-        Var('android_ndk_revision'),
-    'condition': 'checkout_android',
+  'third_party/android_toolchain': {
+    'packages': [
+      {
+        'package': 'chromium/third_party/android_toolchain/android_toolchain',
+        'version': Var('android_toolchain_version'),
+      },
+    ],
+    'condition': 'checkout_android_native_support',
+    'dep_type': 'cipd',
   },
 
   'third_party/catapult': {
@@ -270,6 +303,13 @@
     'condition': 'checkout_android',
   },
 
+  'third_party/cpu_features/src': {
+    'url': Var('chromium_git') +
+        '/external/github.com/google/cpu_features.git@' +
+        Var('cpu_features_revision'),
+    'condition': 'checkout_android',
+  },
+
   'third_party/depot_tools':
     Var('chromium_git') + '/chromium/tools/depot_tools.git@' +
         Var('depot_tools_revision'),
@@ -278,15 +318,11 @@
     Var('chromium_git') + '/chromium/src/third_party/freetype2.git@' +
         Var('freetype_revision'),
 
-  'third_party/fuchsia-sdk/sdk': {
-      'packages': [
-          {
-              'package': Var('fuchsia_sdk_cipd_prefix') + '${{platform}}',
-              'version': Var('fuchsia_version'),
-          },
-      ],
-      'condition': 'checkout_fuchsia',
-      'dep_type': 'cipd',
+  'third_party/fuchsia-gn-sdk': {
+    'url': Var('chromium_git') +
+        '/chromium/src/third_party/fuchsia-gn-sdk.git@' +
+        Var('fuchsia_gn_sdk_revision'),
+    'condition': 'checkout_fuchsia',
   },
 
   'third_party/googletest/src':
@@ -305,6 +341,21 @@
     Var('chromium_git') + '/chromium/src/third_party/jinja2.git@' +
         Var('jinja2_revision'),
 
+  'third_party/libc++/src':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libcxx.git@' +
+        Var('libcxx_revision'),
+
+  'third_party/libc++abi/src':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libcxxabi.git@' +
+        Var('libcxxabi_revision'),
+
+  'third_party/libunwind/src':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libunwind.git@' +
+        Var('libunwind_revision'),
+
   'third_party/libjpeg_turbo':
     Var('chromium_git') + '/chromium/deps/libjpeg_turbo.git@' +
         Var('jpeg_turbo_revision'),
@@ -313,11 +364,6 @@
     Var('chromium_git') + '/chromium/src/third_party/libpng.git@' +
         Var('libpng_revision'),
 
-  'third_party/lss': {
-      'url': Var('chromium_git') + '/linux-syscall-support.git' + '@' + Var('lss_revision'),
-      'condition': 'checkout_android or checkout_linux',
-  },
-
   'third_party/markupsafe':
     Var('chromium_git') + '/chromium/src/third_party/markupsafe.git@' +
         Var('markupsafe_revision'),
@@ -651,4 +697,32 @@
     'action': ['python3', 'build/util/lastchange.py',
                '-o', 'build/util/LASTCHANGE'],
   },
+  {
+    'name': 'Download Fuchsia SDK from GCS',
+    'pattern': '.',
+    'condition': 'checkout_fuchsia',
+    'action': [
+      'python3',
+      'build/fuchsia/update_sdk.py',
+      '--cipd-prefix={fuchsia_sdk_cipd_prefix}',
+      '--version={fuchsia_version}',
+    ],
+  },
+  # Download remote exec cfg files
+  {
+    'name': 'fetch_reclient_cfgs',
+    'pattern': '.',
+    'condition': 'download_remoteexec_cfg',
+    'action': ['python3',
+               'buildtools/reclient_cfgs/fetch_reclient_cfgs.py',
+               '--rbe_instance',
+               Var('rbe_instance'),
+               '--reproxy_cfg_template',
+               'reproxy.cfg.template',
+               '--rewrapper_cfg_project',
+               Var('rewrapper_cfg_project'),
+               '--quiet',
+               '--hook',
+               ],
+  },
 ]
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 236f896..14c0506 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -428,12 +428,12 @@
   """Checks that .png files have the right file name format, which must be in
   the form:
 
-  NAME_expected(_(agg|skia))?(_(linux|mac|win))?.pdf.\d+.png
+  NAME_expected(_(agg|gdi|skia))?(_(linux|mac|win))?.pdf.\d+.png
 
   This must be the same format as the one in testing/corpus's PRESUBMIT.py.
   """
   expected_pattern = input_api.re.compile(
-      r'.+_expected(_(agg|skia))?(_(linux|mac|win))?\.pdf\.\d+.png')
+      r'.+_expected(_(agg|gdi|skia))?(_(linux|mac|win))?\.pdf\.\d+.png')
   results = []
   for f in input_api.AffectedFiles(include_deletes=False):
     if not f.LocalPath().endswith('.png'):
diff --git a/README.md b/README.md
index 657cfda..d867e4c 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@
 A typical `<directory>` name is `out/Debug`.
 
 ```
-use_goma = true  # Googlers only. Make sure goma is installed and running first.
+use_goma = false  # Googlers only. Ensure goma is installed and running first.
 is_debug = true  # Enable debugging features.
 
 # Set true to enable experimental Skia backend.
@@ -95,6 +95,11 @@
 
 By default, the entire project builds with C++17.
 
+By default, PDFium expects to build with a clang compiler that provides
+additional chrome plugins. To build against a vanilla one lacking these,
+one must set
+`clang_use_chrome_plugins = false`.
+
 When complete the arguments will be stored in `<directory>/args.gn`, and
 GN will automatically use the new arguments to generate build files.
 Should your files fail to generate, please double-check that you have set
diff --git a/build_overrides/BUILDCONFIG.gn b/build_overrides/BUILDCONFIG.gn
index 2b9bbd6..d5ced94 100644
--- a/build_overrides/BUILDCONFIG.gn
+++ b/build_overrides/BUILDCONFIG.gn
@@ -369,6 +369,10 @@
   ]
 }
 
+if (is_apple) {
+  default_compiler_configs += [ "//build/config/compiler:enable_arc2" ]
+}
+
 if (is_posix) {
   if (current_os != "aix") {
     default_compiler_configs +=
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index 446fa89..b10e681 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -6,11 +6,6 @@
 # Chromium specific targets in a client project's GN file etc.
 build_with_chromium = false
 
-# Support different NDK locations in non-Chromium builds.
-default_android_ndk_root = "//third_party/android_ndk"
-default_android_ndk_version = "r16"
-default_android_ndk_major_version = 16
-
 # PDFium builds don't support building java targets.
 enable_java_templates = false
 
diff --git a/build_overrides/partition_alloc.gni b/build_overrides/partition_alloc.gni
index 0cf7009..56f913c 100644
--- a/build_overrides/partition_alloc.gni
+++ b/build_overrides/partition_alloc.gni
@@ -2,10 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/sanitizers/sanitizers.gni")
+
+# Sanitizers replace the allocator, don't use our own allocator.
+_is_using_sanitizers = is_asan || is_hwasan || is_lsan || is_tsan || is_msan
+
+# The allocator shim isn't working standalone on Windows at the moment.
+# TODO(https://crbug.com/pdfium/2068) - make work with windows.
+_use_shim = !_is_using_sanitizers && !is_win
+
 # See base/allocator/partition_allocator/external_builds.md
-use_partition_alloc_as_malloc_default = false
+use_allocator_shim_default = _use_shim
+use_partition_alloc_as_malloc_default = _use_shim
+enable_backup_ref_ptr_support_default = _use_shim
 enable_mte_checked_ptr_support_default = false
-enable_backup_ref_ptr_support_default = false
 put_ref_count_in_previous_slot_default = false
 enable_backup_ref_ptr_slow_checks_default = false
 enable_dangling_raw_ptr_checks_default = false
diff --git a/core/fdrm/fx_crypt.h b/core/fdrm/fx_crypt.h
index f3171c7..511972f 100644
--- a/core/fdrm/fx_crypt.h
+++ b/core/fdrm/fx_crypt.h
@@ -9,7 +9,7 @@
 
 #include <stdint.h>
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 struct CRYPT_rc4_context {
   static constexpr int32_t kPermutationLength = 256;
diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.h b/core/fpdfapi/cmaps/fpdf_cmaps.h
index 079459a..795427f 100644
--- a/core/fpdfapi/cmaps/fpdf_cmaps.h
+++ b/core/fpdfapi/cmaps/fpdf_cmaps.h
@@ -9,6 +9,8 @@
 
 #include <stdint.h>
 
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+
 namespace fxcmap {
 
 struct DWordCIDMap {
@@ -21,9 +23,9 @@
 struct CMap {
   enum class Type : bool { kSingle, kRange };
 
-  const char* m_Name;              // Raw, POD struct.
-  const uint16_t* m_pWordMap;      // Raw, POD struct.
-  const DWordCIDMap* m_pDWordMap;  // Raw, POD struct.
+  UNOWNED_PTR_EXCLUSION const char* m_Name;              // POD struct.
+  UNOWNED_PTR_EXCLUSION const uint16_t* m_pWordMap;      // POD struct.
+  UNOWNED_PTR_EXCLUSION const DWordCIDMap* m_pDWordMap;  // POD struct.
   uint16_t m_WordCount;
   uint16_t m_DWordCount;
   Type m_WordMapType;
diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn
index 721dff8..f6af621 100644
--- a/core/fpdfapi/edit/BUILD.gn
+++ b/core/fpdfapi/edit/BUILD.gn
@@ -24,7 +24,6 @@
   ]
   deps = [
     "../../../constants",
-    "../../../third_party:skia_shared",
     "../../fxcrt",
     "../font",
     "../page",
diff --git a/core/fpdfapi/edit/DEPS b/core/fpdfapi/edit/DEPS
deleted file mode 100644
index bcfd0a2..0000000
--- a/core/fpdfapi/edit/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-specific_include_rules = {
-  "cpdf_contentstream_write_utils.cpp": [
-    '+third_party/skia_shared',
-  ]
-}
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
index af6a169..d1fede9 100644
--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
@@ -4,13 +4,220 @@
 
 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 
+#include <cassert>
+#include <cfloat>
+#include <climits>
+#include <cmath>
 #include <ostream>
 
-#include "third_party/skia_shared/SkFloatToDecimal.h"
+namespace {
+
+constexpr unsigned kMaximumSkFloatToDecimalLength = 49;
+
+// Return pow(10.0, e), optimized for common cases.
+double pow10(int e) {
+  switch (e) {
+    case 0:
+      return 1.0;  // common cases
+    case 1:
+      return 10.0;
+    case 2:
+      return 100.0;
+    case 3:
+      return 1e+03;
+    case 4:
+      return 1e+04;
+    case 5:
+      return 1e+05;
+    case 6:
+      return 1e+06;
+    case 7:
+      return 1e+07;
+    case 8:
+      return 1e+08;
+    case 9:
+      return 1e+09;
+    case 10:
+      return 1e+10;
+    case 11:
+      return 1e+11;
+    case 12:
+      return 1e+12;
+    case 13:
+      return 1e+13;
+    case 14:
+      return 1e+14;
+    case 15:
+      return 1e+15;
+    default:
+      if (e > 15) {
+        double value = 1e+15;
+        while (e-- > 15) {
+          value *= 10.0;
+        }
+        return value;
+      } else {
+        assert(e < 0);
+        double value = 1.0;
+        while (e++ < 0) {
+          value /= 10.0;
+        }
+        return value;
+      }
+  }
+}
+
+// SkFloatToDecimal
+//
+// Convert a float into a decimal string.
+//
+// The resulting string will be in the form `[-]?([0-9]*\.)?[0-9]+` (It does
+// not use scientific notation.) and `sscanf(output, "%f", &x)` will return
+// the original value if the value is finite. This function accepts all
+// possible input values.
+//
+// INFINITY and -INFINITY are rounded to FLT_MAX and -FLT_MAX.
+//
+// NAN values are converted to 0.
+//
+// This function will always add a terminating '\0' to the output.
+//
+// @param value  Any floating-point number
+// @param output The buffer to write the string into.  Must be non-null.
+//
+// @return strlen(output)
+//
+// Write a string into output, including a terminating '\0' (for
+// unit testing).  Return strlen(output) (for SkWStream::write) The
+// resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
+// sscanf(output, "%f", &x) will return the original value iff the
+// value is finite. This function accepts all possible input values.
+//
+// Motivation: "PDF does not support [numbers] in exponential format
+// (such as 6.02e23)."  Otherwise, this function would rely on a
+// sprintf-type function from the standard library.
+unsigned SkFloatToDecimal(float value,
+                          char output[kMaximumSkFloatToDecimalLength]) {
+  // The longest result is -FLT_MIN.
+  // We serialize it as "-.0000000000000000000000000000000000000117549435"
+  // which has 48 characters plus a terminating '\0'.
+
+  static_assert(kMaximumSkFloatToDecimalLength == 49, "");
+  // 3 = '-', '.', and '\0' characters.
+  // 9 = number of significant digits
+  // abs(FLT_MIN_10_EXP) = number of zeros in FLT_MIN
+  static_assert(kMaximumSkFloatToDecimalLength == 3 + 9 - FLT_MIN_10_EXP, "");
+
+  // section C.1 of the PDF 1.4 spec (http://goo.gl/0SCswJ) says that
+  // most PDF rasterizers will use fixed-point scalars that lack the
+  // dynamic range of floats.  Even if this is the case, I want to
+  // serialize these (uncommon) very small and very large scalar
+  // values with enough precision to allow a floating-point
+  // rasterizer to read them in with perfect accuracy.
+  // Experimentally, rasterizers such as pdfium do seem to benefit
+  // from this.  Rasterizers that rely on fixed-point scalars should
+  // gracefully ignore these values that they can not parse.
+  char* output_ptr = &output[0];
+  const char* const end = &output[kMaximumSkFloatToDecimalLength - 1];
+  // subtract one to leave space for '\0'.
+
+  // This function is written to accept any possible input value,
+  // including non-finite values such as INF and NAN.  In that case,
+  // we ignore value-correctness and output a syntacticly-valid
+  // number.
+  if (value == INFINITY) {
+    value = FLT_MAX;  // nearest finite float.
+  }
+  if (value == -INFINITY) {
+    value = -FLT_MAX;  // nearest finite float.
+  }
+  if (!std::isfinite(value) || value == 0.0f) {
+    // NAN is unsupported in PDF.  Always output a valid number.
+    // Also catch zero here, as a special case.
+    *output_ptr++ = '0';
+    *output_ptr = '\0';
+    return static_cast<unsigned>(output_ptr - output);
+  }
+  if (value < 0.0) {
+    *output_ptr++ = '-';
+    value = -value;
+  }
+  assert(value >= 0.0f);
+
+  int binaryExponent;
+  (void)std::frexp(value, &binaryExponent);
+  static const double kLog2 = 0.3010299956639812;  // log10(2.0);
+  int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
+  int decimalShift = decimalExponent - 8;
+  double power = pow10(-decimalShift);
+  assert(value * power <= (double)INT_MAX);
+  int d = static_cast<int>(value * power + 0.5);
+  // assert(value == (float)(d * pow(10.0, decimalShift)));
+  assert(d <= 999999999);
+  if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
+    // need one fewer decimal digits for 24-bit precision.
+    decimalShift = decimalExponent - 7;
+    // assert(power * 0.1 = pow10(-decimalShift));
+    // recalculate to get rounding right.
+    d = static_cast<int>(value * (power * 0.1) + 0.5);
+    assert(d <= 99999999);
+  }
+  while (d % 10 == 0) {
+    d /= 10;
+    ++decimalShift;
+  }
+  assert(d > 0);
+  // assert(value == (float)(d * pow(10.0, decimalShift)));
+  unsigned char buffer[9];  // decimal value buffer.
+  int bufferIndex = 0;
+  do {
+    buffer[bufferIndex++] = d % 10;
+    d /= 10;
+  } while (d != 0);
+  assert(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
+  if (decimalShift >= 0) {
+    do {
+      --bufferIndex;
+      *output_ptr++ = '0' + buffer[bufferIndex];
+    } while (bufferIndex);
+    for (int i = 0; i < decimalShift; ++i) {
+      *output_ptr++ = '0';
+    }
+  } else {
+    int placesBeforeDecimal = bufferIndex + decimalShift;
+    if (placesBeforeDecimal > 0) {
+      while (placesBeforeDecimal-- > 0) {
+        --bufferIndex;
+        *output_ptr++ = '0' + buffer[bufferIndex];
+      }
+      *output_ptr++ = '.';
+    } else {
+      *output_ptr++ = '.';
+      int placesAfterDecimal = -placesBeforeDecimal;
+      while (placesAfterDecimal-- > 0) {
+        *output_ptr++ = '0';
+      }
+    }
+    while (bufferIndex > 0) {
+      --bufferIndex;
+      *output_ptr++ = '0' + buffer[bufferIndex];
+      if (output_ptr == end) {
+        break;  // denormalized: don't need extra precision.
+                // Note: denormalized numbers will not have the same number of
+                // significantDigits, but do not need them to round-trip.
+      }
+    }
+  }
+  assert(output_ptr <= end);
+  *output_ptr = '\0';
+  return static_cast<unsigned>(output_ptr - output);
+}
+
+}  // namespace
 
 std::ostream& WriteFloat(std::ostream& stream, float value) {
-  char buffer[pdfium::skia::kMaximumSkFloatToDecimalLength];
-  unsigned size = pdfium::skia::SkFloatToDecimal(value, buffer);
+  char buffer[kMaximumSkFloatToDecimalLength];
+  unsigned size = SkFloatToDecimal(value, buffer);
   stream.write(buffer, size);
   return stream;
 }
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 8717028..be87afa 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -41,9 +41,9 @@
 #include "core/fpdfapi/parser/object_tree_traversal_util.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 namespace {
 
@@ -81,10 +81,9 @@
         break;
     }
   }
-  const ByteString& graphics_resource_name =
-      page_object->GetGraphicsResourceName();
-  if (!graphics_resource_name.IsEmpty()) {
-    seen_resources["ExtGState"].insert(graphics_resource_name);
+  for (const auto& name : page_object->GetGraphicsResourceNames()) {
+    CHECK(!name.IsEmpty());
+    seen_resources["ExtGState"].insert(name);
   }
 }
 
@@ -362,9 +361,8 @@
         *buf << "/" << item->GetPropertyName() << " ";
         break;
       }
-      default:
-        NOTREACHED();
-        break;
+      case CPDF_ContentMarkItem::kNone:
+        NOTREACHED_NORETURN();
     }
 
     // Write BDC (begin dictionary content) operator.
@@ -548,6 +546,18 @@
   CFX_GraphStateData::LineJoin lineJoin = pPageObj->m_GraphState.GetLineJoin();
   if (lineJoin != CFX_GraphStateData::LineJoin::kMiter)
     *buf << static_cast<int>(lineJoin) << " j ";
+  std::vector<float> dash_array = pPageObj->m_GraphState.GetLineDashArray();
+  if (dash_array.size()) {
+    *buf << "[";
+    for (size_t i = 0; i < dash_array.size(); ++i) {
+      if (i > 0) {
+        *buf << " ";
+      }
+      WriteFloat(*buf, dash_array[i]);
+    }
+    *buf << "] ";
+    WriteFloat(*buf, pPageObj->m_GraphState.GetLineDashPhase()) << " d ";
+  }
 
   const CPDF_ClipPath& clip_path = pPageObj->m_ClipPath;
   if (clip_path.HasRef()) {
@@ -562,8 +572,7 @@
           *buf << " W* ";
           break;
         case CFX_FillRenderOptions::FillType::kNoFill:
-          NOTREACHED();
-          break;
+          NOTREACHED_NORETURN();
       }
 
       // Use a no-op path-painting operator to terminate the path without
@@ -600,7 +609,7 @@
     }
     m_pDocument->AddIndirectObject(gsDict);
     name = RealizeResource(std::move(gsDict), "ExtGState");
-    pPageObj->SetGraphicsResourceName(name);
+    pPageObj->SetGraphicsResourceNames({name});
     m_pObjHolder->GraphicsMapInsert(graphD, name);
   }
   *buf << "/" << PDF_NameEncode(name) << " gs ";
diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
index 4d9f7f2..48033b0 100644
--- a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
@@ -14,8 +14,7 @@
 CPDF_StringArchiveStream::~CPDF_StringArchiveStream() = default;
 
 FX_FILESIZE CPDF_StringArchiveStream::CurrentOffset() const {
-  NOTREACHED();
-  return false;
+  NOTREACHED_NORETURN();
 }
 
 bool CPDF_StringArchiveStream::WriteBlock(pdfium::span<const uint8_t> buffer) {
diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.h b/core/fpdfapi/edit/cpdf_stringarchivestream.h
index 1306343..3907e2c 100644
--- a/core/fpdfapi/edit/cpdf_stringarchivestream.h
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.h
@@ -7,6 +7,7 @@
 
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/fx_string_wrappers.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_StringArchiveStream final : public IFX_ArchiveStream {
  public:
@@ -18,7 +19,7 @@
   FX_FILESIZE CurrentOffset() const override;
 
  private:
-  fxcrt::ostringstream* stream_;
+  UnownedPtr<fxcrt::ostringstream> stream_;
 };
 
 #endif  // CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.cpp b/core/fpdfapi/font/cfx_cttgsubtable.cpp
index ae5a841..c19649e 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.cpp
+++ b/core/fpdfapi/font/cfx_cttgsubtable.cpp
@@ -27,44 +27,53 @@
 
 }  // namespace
 
-CFX_CTTGSUBTable::CFX_CTTGSUBTable(FT_Bytes gsub) {
+CFX_CTTGSUBTable::CFX_CTTGSUBTable(pdfium::span<const uint8_t> gsub) {
   if (!LoadGSUBTable(gsub))
     return;
 
-  for (const TScriptRecord& script : ScriptList) {
-    for (const auto& record : script.LangSysRecords) {
-      for (uint16_t index : record.FeatureIndices) {
-        if (IsVerticalFeatureTag(FeatureList[index].FeatureTag))
-          m_featureSet.insert(index);
+  for (const auto& script : script_list_) {
+    for (const auto& record : script) {
+      for (uint16_t index : record) {
+        if (IsVerticalFeatureTag(feature_list_[index].feature_tag)) {
+          feature_set_.insert(index);
+        }
       }
     }
   }
-  if (!m_featureSet.empty())
+  if (!feature_set_.empty()) {
     return;
+  }
 
   int i = 0;
-  for (const TFeatureRecord& feature : FeatureList) {
-    if (IsVerticalFeatureTag(feature.FeatureTag))
-      m_featureSet.insert(i);
+  for (const FeatureRecord& feature : feature_list_) {
+    if (IsVerticalFeatureTag(feature.feature_tag)) {
+      feature_set_.insert(i);
+    }
     ++i;
   }
 }
 
 CFX_CTTGSUBTable::~CFX_CTTGSUBTable() = default;
 
-bool CFX_CTTGSUBTable::LoadGSUBTable(FT_Bytes gsub) {
+bool CFX_CTTGSUBTable::LoadGSUBTable(pdfium::span<const uint8_t> gsub) {
   if (FXSYS_UINT32_GET_MSBFIRST(gsub) != 0x00010000)
     return false;
 
-  return Parse(&gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 4)],
-               &gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 6)],
-               &gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 8)]);
+  auto scriptlist_span = gsub.subspan(4, 2);
+  auto featurelist_span = gsub.subspan(6, 2);
+  auto lookuplist_span = gsub.subspan(8, 2);
+  size_t scriptlist_index = FXSYS_UINT16_GET_MSBFIRST(scriptlist_span);
+  size_t featurelist_index = FXSYS_UINT16_GET_MSBFIRST(featurelist_span);
+  size_t lookuplist_index = FXSYS_UINT16_GET_MSBFIRST(lookuplist_span);
+  Parse(gsub.subspan(scriptlist_index), gsub.subspan(featurelist_index),
+        gsub.subspan(lookuplist_index));
+  return true;
 }
 
 uint32_t CFX_CTTGSUBTable::GetVerticalGlyph(uint32_t glyphnum) const {
-  for (uint32_t item : m_featureSet) {
+  for (uint32_t item : feature_set_) {
     absl::optional<uint32_t> result =
-        GetVerticalGlyphSub(FeatureList[item], glyphnum);
+        GetVerticalGlyphSub(feature_list_[item], glyphnum);
     if (result.has_value())
       return result.value();
   }
@@ -72,15 +81,17 @@
 }
 
 absl::optional<uint32_t> CFX_CTTGSUBTable::GetVerticalGlyphSub(
-    const TFeatureRecord& feature,
+    const FeatureRecord& feature,
     uint32_t glyphnum) const {
-  for (int index : feature.LookupListIndices) {
-    if (!fxcrt::IndexInBounds(LookupList, index))
+  for (int index : feature.lookup_list_indices) {
+    if (!fxcrt::IndexInBounds(lookup_list_, index)) {
       continue;
-    if (LookupList[index].LookupType != 1)
+    }
+    if (lookup_list_[index].lookup_type != 1) {
       continue;
+    }
     absl::optional<uint32_t> result =
-        GetVerticalGlyphSub2(LookupList[index], glyphnum);
+        GetVerticalGlyphSub2(lookup_list_[index], glyphnum);
     if (result.has_value())
       return result.value();
   }
@@ -88,272 +99,244 @@
 }
 
 absl::optional<uint32_t> CFX_CTTGSUBTable::GetVerticalGlyphSub2(
-    const TLookup& lookup,
+    const Lookup& lookup,
     uint32_t glyphnum) const {
-  for (const auto& subTable : lookup.SubTables) {
-    switch (subTable->SubstFormat) {
-      case 1: {
-        auto* tbl1 = static_cast<TSubTable1*>(subTable.get());
-        if (GetCoverageIndex(tbl1->Coverage.get(), glyphnum) >= 0) {
-          return glyphnum + tbl1->DeltaGlyphID;
-        }
-        break;
+  for (const auto& sub_table : lookup.sub_tables) {
+    if (absl::holds_alternative<absl::monostate>(sub_table.table_data)) {
+      continue;
+    }
+    int index = GetCoverageIndex(sub_table.coverage, glyphnum);
+    if (absl::holds_alternative<int16_t>(sub_table.table_data)) {
+      if (index >= 0) {
+        return glyphnum + absl::get<int16_t>(sub_table.table_data);
       }
-      case 2: {
-        auto* tbl2 = static_cast<TSubTable2*>(subTable.get());
-        int index = GetCoverageIndex(tbl2->Coverage.get(), glyphnum);
-        if (fxcrt::IndexInBounds(tbl2->Substitutes, index)) {
-          return tbl2->Substitutes[index];
-        }
-        break;
+    } else {
+      const auto& substitutes =
+          absl::get<DataVector<uint16_t>>(sub_table.table_data);
+      if (fxcrt::IndexInBounds(substitutes, index)) {
+        return substitutes[index];
       }
     }
   }
   return absl::nullopt;
 }
 
-int CFX_CTTGSUBTable::GetCoverageIndex(TCoverageFormatBase* Coverage,
+int CFX_CTTGSUBTable::GetCoverageIndex(const CoverageFormat& coverage,
                                        uint32_t g) const {
-  if (!Coverage)
+  if (absl::holds_alternative<absl::monostate>(coverage)) {
     return -1;
+  }
 
-  switch (Coverage->CoverageFormat) {
-    case 1: {
-      int i = 0;
-      TCoverageFormat1* c1 = static_cast<TCoverageFormat1*>(Coverage);
-      for (const auto& glyph : c1->GlyphArray) {
-        if (static_cast<uint32_t>(glyph) == g)
-          return i;
-        ++i;
+  if (absl::holds_alternative<DataVector<uint16_t>>(coverage)) {
+    int i = 0;
+    const auto& glyph_array = absl::get<DataVector<uint16_t>>(coverage);
+    for (const auto& glyph : glyph_array) {
+      if (static_cast<uint32_t>(glyph) == g) {
+        return i;
       }
-      return -1;
+      ++i;
     }
-    case 2: {
-      TCoverageFormat2* c2 = static_cast<TCoverageFormat2*>(Coverage);
-      for (const auto& rangeRec : c2->RangeRecords) {
-        uint32_t s = rangeRec.Start;
-        uint32_t e = rangeRec.End;
-        uint32_t si = rangeRec.StartCoverageIndex;
-        if (s <= g && g <= e)
-          return si + g - s;
-      }
-      return -1;
+    return -1;
+  }
+
+  const auto& range_records = absl::get<std::vector<RangeRecord>>(coverage);
+  for (const auto& range_rec : range_records) {
+    uint32_t s = range_rec.start;
+    uint32_t e = range_rec.end;
+    uint32_t si = range_rec.start_coverage_index;
+    if (s <= g && g <= e) {
+      return si + g - s;
     }
   }
   return -1;
 }
 
-uint8_t CFX_CTTGSUBTable::GetUInt8(FT_Bytes& p) const {
+uint8_t CFX_CTTGSUBTable::GetUInt8(const uint8_t*& p) const {
   uint8_t ret = p[0];
   p += 1;
   return ret;
 }
 
-int16_t CFX_CTTGSUBTable::GetInt16(FT_Bytes& p) const {
+int16_t CFX_CTTGSUBTable::GetInt16(const uint8_t*& p) const {
   uint16_t ret = FXSYS_UINT16_GET_MSBFIRST(p);
   p += 2;
-  return *(int16_t*)&ret;
+  return *reinterpret_cast<int16_t*>(&ret);
 }
 
-uint16_t CFX_CTTGSUBTable::GetUInt16(FT_Bytes& p) const {
+uint16_t CFX_CTTGSUBTable::GetUInt16(const uint8_t*& p) const {
   uint16_t ret = FXSYS_UINT16_GET_MSBFIRST(p);
   p += 2;
   return ret;
 }
 
-int32_t CFX_CTTGSUBTable::GetInt32(FT_Bytes& p) const {
+int32_t CFX_CTTGSUBTable::GetInt32(const uint8_t*& p) const {
   uint32_t ret = FXSYS_UINT32_GET_MSBFIRST(p);
   p += 4;
-  return *(int32_t*)&ret;
+  return *reinterpret_cast<int32_t*>(&ret);
 }
 
-uint32_t CFX_CTTGSUBTable::GetUInt32(FT_Bytes& p) const {
+uint32_t CFX_CTTGSUBTable::GetUInt32(const uint8_t*& p) const {
   uint32_t ret = FXSYS_UINT32_GET_MSBFIRST(p);
   p += 4;
   return ret;
 }
 
-bool CFX_CTTGSUBTable::Parse(FT_Bytes scriptlist,
-                             FT_Bytes featurelist,
-                             FT_Bytes lookuplist) {
+void CFX_CTTGSUBTable::Parse(pdfium::span<const uint8_t> scriptlist,
+                             pdfium::span<const uint8_t> featurelist,
+                             pdfium::span<const uint8_t> lookuplist) {
   ParseScriptList(scriptlist);
   ParseFeatureList(featurelist);
   ParseLookupList(lookuplist);
-  return true;
 }
 
-void CFX_CTTGSUBTable::ParseScriptList(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  ScriptList = std::vector<TScriptRecord>(GetUInt16(sp));
-  for (auto& scriptRec : ScriptList) {
-    scriptRec.ScriptTag = GetUInt32(sp);
-    ParseScript(&raw[GetUInt16(sp)], &scriptRec);
+void CFX_CTTGSUBTable::ParseScriptList(pdfium::span<const uint8_t> raw) {
+  const uint8_t* sp = raw.data();
+  script_list_ = std::vector<ScriptRecord>(GetUInt16(sp));
+  for (auto& script : script_list_) {
+    // Skip over "ScriptTag" field.
+    sp += 4;
+    script = ParseScript(&raw[GetUInt16(sp)]);
   }
 }
 
-void CFX_CTTGSUBTable::ParseScript(FT_Bytes raw, TScriptRecord* rec) {
-  FT_Bytes sp = raw;
-  rec->DefaultLangSys = GetUInt16(sp);
-  rec->LangSysRecords = std::vector<TLangSysRecord>(GetUInt16(sp));
-  for (auto& sysRecord : rec->LangSysRecords) {
-    sysRecord.LangSysTag = GetUInt32(sp);
-    ParseLangSys(&raw[GetUInt16(sp)], &sysRecord);
+CFX_CTTGSUBTable::ScriptRecord CFX_CTTGSUBTable::ParseScript(
+    const uint8_t* raw) {
+  // Skip over "DefaultLangSys" field.
+  const uint8_t* sp = raw + 2;
+  ScriptRecord result(GetUInt16(sp));
+  for (auto& record : result) {
+    // Skip over "LangSysTag" field.
+    sp += 4;
+    record = ParseLangSys(&raw[GetUInt16(sp)]);
   }
+  return result;
 }
 
-void CFX_CTTGSUBTable::ParseLangSys(FT_Bytes raw, TLangSysRecord* rec) {
-  FT_Bytes sp = raw;
-  rec->LookupOrder = GetUInt16(sp);
-  rec->ReqFeatureIndex = GetUInt16(sp);
-  rec->FeatureIndices = DataVector<uint16_t>(GetUInt16(sp));
-  for (auto& element : rec->FeatureIndices)
+CFX_CTTGSUBTable::FeatureIndices CFX_CTTGSUBTable::ParseLangSys(
+    const uint8_t* raw) {
+  // Skip over "LookupOrder" and "ReqFeatureIndex" fields.
+  const uint8_t* sp = raw + 4;
+  FeatureIndices result(GetUInt16(sp));
+  for (auto& element : result) {
     element = GetUInt16(sp);
+  }
+  return result;
 }
 
-void CFX_CTTGSUBTable::ParseFeatureList(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  FeatureList = std::vector<TFeatureRecord>(GetUInt16(sp));
-  for (auto& featureRec : FeatureList) {
-    featureRec.FeatureTag = GetUInt32(sp);
-    ParseFeature(&raw[GetUInt16(sp)], &featureRec);
+void CFX_CTTGSUBTable::ParseFeatureList(pdfium::span<const uint8_t> raw) {
+  const uint8_t* sp = raw.data();
+  feature_list_ = std::vector<FeatureRecord>(GetUInt16(sp));
+  for (auto& record : feature_list_) {
+    record.feature_tag = GetUInt32(sp);
+    record.lookup_list_indices =
+        ParseFeatureLookupListIndices(&raw[GetUInt16(sp)]);
   }
 }
 
-void CFX_CTTGSUBTable::ParseFeature(FT_Bytes raw, TFeatureRecord* rec) {
-  FT_Bytes sp = raw;
-  rec->FeatureParams = GetUInt16(sp);
-  rec->LookupListIndices = DataVector<uint16_t>(GetUInt16(sp));
-  for (auto& listIndex : rec->LookupListIndices)
-    listIndex = GetUInt16(sp);
+DataVector<uint16_t> CFX_CTTGSUBTable::ParseFeatureLookupListIndices(
+    const uint8_t* raw) {
+  // Skip over "FeatureParams" field.
+  const uint8_t* sp = raw + 2;
+  DataVector<uint16_t> result(GetUInt16(sp));
+  for (auto& index : result) {
+    index = GetUInt16(sp);
+  }
+  return result;
 }
 
-void CFX_CTTGSUBTable::ParseLookupList(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  LookupList = std::vector<TLookup>(GetUInt16(sp));
-  for (auto& lookup : LookupList)
-    ParseLookup(&raw[GetUInt16(sp)], &lookup);
+void CFX_CTTGSUBTable::ParseLookupList(pdfium::span<const uint8_t> raw) {
+  const uint8_t* sp = raw.data();
+  lookup_list_ = std::vector<Lookup>(GetUInt16(sp));
+  for (auto& lookup : lookup_list_) {
+    lookup = ParseLookup(&raw[GetUInt16(sp)]);
+  }
 }
 
-void CFX_CTTGSUBTable::ParseLookup(FT_Bytes raw, TLookup* rec) {
-  FT_Bytes sp = raw;
-  rec->LookupType = GetUInt16(sp);
-  rec->LookupFlag = GetUInt16(sp);
-  rec->SubTables = std::vector<std::unique_ptr<TSubTableBase>>(GetUInt16(sp));
-  if (rec->LookupType != 1)
-    return;
+CFX_CTTGSUBTable::Lookup CFX_CTTGSUBTable::ParseLookup(const uint8_t* raw) {
+  const uint8_t* sp = raw;
+  CFX_CTTGSUBTable::Lookup result;
+  result.lookup_type = GetUInt16(sp);
+  // Skip over "LookupFlag" field.
+  sp += 2;
+  result.sub_tables = Lookup::SubTables(GetUInt16(sp));
+  if (result.lookup_type != 1) {
+    return result;
+  }
 
-  for (auto& subTable : rec->SubTables)
-    subTable = ParseSingleSubst(&raw[GetUInt16(sp)]);
+  for (auto& sub_table : result.sub_tables) {
+    sub_table = ParseSingleSubst(&raw[GetUInt16(sp)]);
+  }
+  return result;
 }
 
-std::unique_ptr<CFX_CTTGSUBTable::TCoverageFormatBase>
-CFX_CTTGSUBTable::ParseCoverage(FT_Bytes raw) {
-  FT_Bytes sp = raw;
+CFX_CTTGSUBTable::CoverageFormat CFX_CTTGSUBTable::ParseCoverage(
+    const uint8_t* raw) {
+  const uint8_t* sp = raw;
   uint16_t format = GetUInt16(sp);
-  if (format == 1)
-    return ParseCoverageFormat1(raw);
-  if (format == 2)
-    return ParseCoverageFormat2(raw);
-  return nullptr;
+  if (format != 1 && format != 2) {
+    return absl::monostate();
+  }
+
+  if (format == 1) {
+    DataVector<uint16_t> glyph_array(GetUInt16(sp));
+    for (auto& glyph : glyph_array) {
+      glyph = GetUInt16(sp);
+    }
+    return glyph_array;
+  }
+
+  std::vector<RangeRecord> range_records(GetUInt16(sp));
+  for (auto& range_rec : range_records) {
+    range_rec.start = GetUInt16(sp);
+    range_rec.end = GetUInt16(sp);
+    range_rec.start_coverage_index = GetUInt16(sp);
+  }
+  return range_records;
 }
 
-std::unique_ptr<CFX_CTTGSUBTable::TCoverageFormat1>
-CFX_CTTGSUBTable::ParseCoverageFormat1(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  (void)GetUInt16(sp);
-  auto rec = std::make_unique<TCoverageFormat1>(GetUInt16(sp));
-  for (auto& glyph : rec->GlyphArray)
-    glyph = GetUInt16(sp);
-  return rec;
-}
+CFX_CTTGSUBTable::SubTable CFX_CTTGSUBTable::ParseSingleSubst(
+    const uint8_t* raw) {
+  const uint8_t* sp = raw;
+  uint16_t format = GetUInt16(sp);
+  SubTable rec;
+  if (format != 1 && format != 2) {
+    return rec;
+  }
 
-std::unique_ptr<CFX_CTTGSUBTable::TCoverageFormat2>
-CFX_CTTGSUBTable::ParseCoverageFormat2(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  (void)GetUInt16(sp);
-  auto rec = std::make_unique<TCoverageFormat2>(GetUInt16(sp));
-  for (auto& rangeRec : rec->RangeRecords) {
-    rangeRec.Start = GetUInt16(sp);
-    rangeRec.End = GetUInt16(sp);
-    rangeRec.StartCoverageIndex = GetUInt16(sp);
+  uint16_t offset = GetUInt16(sp);
+  rec.coverage = ParseCoverage(&raw[offset]);
+  if (format == 1) {
+    rec.table_data = GetInt16(sp);
+  } else {
+    DataVector<uint16_t> table_data(GetUInt16(sp));
+    for (auto& substitute : table_data) {
+      substitute = GetUInt16(sp);
+    }
+    rec.table_data = std::move(table_data);
   }
   return rec;
 }
 
-std::unique_ptr<CFX_CTTGSUBTable::TSubTableBase>
-CFX_CTTGSUBTable::ParseSingleSubst(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  uint16_t format = GetUInt16(sp);
-  if (format == 1)
-    return ParseSingleSubstFormat1(raw);
-  if (format == 2)
-    return ParseSingleSubstFormat2(raw);
-  return nullptr;
-}
+CFX_CTTGSUBTable::FeatureRecord::FeatureRecord() = default;
 
-std::unique_ptr<CFX_CTTGSUBTable::TSubTable1>
-CFX_CTTGSUBTable::ParseSingleSubstFormat1(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  GetUInt16(sp);
-  uint16_t offset = GetUInt16(sp);
-  auto rec = std::make_unique<TSubTable1>();
-  rec->Coverage = ParseCoverage(&raw[offset]);
-  rec->DeltaGlyphID = GetInt16(sp);
-  return rec;
-}
+CFX_CTTGSUBTable::FeatureRecord::~FeatureRecord() = default;
 
-std::unique_ptr<CFX_CTTGSUBTable::TSubTable2>
-CFX_CTTGSUBTable::ParseSingleSubstFormat2(FT_Bytes raw) {
-  FT_Bytes sp = raw;
-  (void)GetUInt16(sp);
-  uint16_t offset = GetUInt16(sp);
-  auto rec = std::make_unique<TSubTable2>();
-  rec->Coverage = ParseCoverage(&raw[offset]);
-  rec->Substitutes = DataVector<uint16_t>(GetUInt16(sp));
-  for (auto& substitute : rec->Substitutes)
-    substitute = GetUInt16(sp);
-  return rec;
-}
+CFX_CTTGSUBTable::RangeRecord::RangeRecord() = default;
 
-CFX_CTTGSUBTable::TLangSysRecord::TLangSysRecord() = default;
+CFX_CTTGSUBTable::SubTable::SubTable() = default;
 
-CFX_CTTGSUBTable::TLangSysRecord::~TLangSysRecord() = default;
+CFX_CTTGSUBTable::SubTable::SubTable(SubTable&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TScriptRecord::TScriptRecord() = default;
+CFX_CTTGSUBTable::SubTable& CFX_CTTGSUBTable::SubTable::operator=(
+    SubTable&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TScriptRecord::~TScriptRecord() = default;
+CFX_CTTGSUBTable::SubTable::~SubTable() = default;
 
-CFX_CTTGSUBTable::TFeatureRecord::TFeatureRecord() = default;
+CFX_CTTGSUBTable::Lookup::Lookup() = default;
 
-CFX_CTTGSUBTable::TFeatureRecord::~TFeatureRecord() = default;
+CFX_CTTGSUBTable::Lookup::Lookup(Lookup&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TRangeRecord::TRangeRecord() = default;
+CFX_CTTGSUBTable::Lookup& CFX_CTTGSUBTable::Lookup::operator=(
+    Lookup&& that) noexcept = default;
 
-CFX_CTTGSUBTable::TCoverageFormat1::TCoverageFormat1(size_t initial_size)
-    : TCoverageFormatBase(1), GlyphArray(initial_size) {}
-
-CFX_CTTGSUBTable::TCoverageFormat1::~TCoverageFormat1() = default;
-
-CFX_CTTGSUBTable::TCoverageFormat2::TCoverageFormat2(size_t initial_size)
-    : TCoverageFormatBase(2), RangeRecords(initial_size) {}
-
-CFX_CTTGSUBTable::TCoverageFormat2::~TCoverageFormat2() = default;
-
-CFX_CTTGSUBTable::TDevice::TDevice() = default;
-
-CFX_CTTGSUBTable::TSubTableBase::TSubTableBase(uint16_t format)
-    : SubstFormat(format) {}
-
-CFX_CTTGSUBTable::TSubTableBase::~TSubTableBase() = default;
-
-CFX_CTTGSUBTable::TSubTable1::TSubTable1() : TSubTableBase(1) {}
-
-CFX_CTTGSUBTable::TSubTable1::~TSubTable1() = default;
-
-CFX_CTTGSUBTable::TSubTable2::TSubTable2() : TSubTableBase(2) {}
-
-CFX_CTTGSUBTable::TSubTable2::~TSubTable2() = default;
-
-CFX_CTTGSUBTable::TLookup::TLookup() = default;
-
-CFX_CTTGSUBTable::TLookup::~TLookup() = default;
+CFX_CTTGSUBTable::Lookup::~Lookup() = default;
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.h b/core/fpdfapi/font/cfx_cttgsubtable.h
index 0555333..2a93cb9 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.h
+++ b/core/fpdfapi/font/cfx_cttgsubtable.h
@@ -9,150 +9,105 @@
 
 #include <stdint.h>
 
-#include <memory>
 #include <set>
 #include <vector>
 
 #include "core/fxcrt/data_vector.h"
 #include "core/fxge/freetype/fx_freetype.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_CTTGSUBTable {
  public:
-  explicit CFX_CTTGSUBTable(FT_Bytes gsub);
+  explicit CFX_CTTGSUBTable(pdfium::span<const uint8_t> gsub);
   ~CFX_CTTGSUBTable();
 
   uint32_t GetVerticalGlyph(uint32_t glyphnum) const;
 
  private:
-  struct TLangSysRecord {
-    TLangSysRecord();
-    ~TLangSysRecord();
+  using FeatureIndices = DataVector<uint16_t>;
+  using ScriptRecord = std::vector<FeatureIndices>;
 
-    uint32_t LangSysTag = 0;
-    uint16_t LookupOrder = 0;
-    uint16_t ReqFeatureIndex = 0;
-    DataVector<uint16_t> FeatureIndices;
+  struct FeatureRecord {
+    FeatureRecord();
+    ~FeatureRecord();
+
+    uint32_t feature_tag = 0;
+    DataVector<uint16_t> lookup_list_indices;
   };
 
-  struct TScriptRecord {
-    TScriptRecord();
-    ~TScriptRecord();
+  struct RangeRecord {
+    RangeRecord();
 
-    uint32_t ScriptTag = 0;
-    uint16_t DefaultLangSys = 0;
-    std::vector<TLangSysRecord> LangSysRecords;
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t start_coverage_index = 0;
   };
 
-  struct TFeatureRecord {
-    TFeatureRecord();
-    ~TFeatureRecord();
+  // GlyphArray for format 1.
+  // RangeRecords for format 2.
+  using CoverageFormat = absl::
+      variant<absl::monostate, DataVector<uint16_t>, std::vector<RangeRecord>>;
 
-    uint32_t FeatureTag = 0;
-    uint16_t FeatureParams = 0;
-    DataVector<uint16_t> LookupListIndices;
+  struct SubTable {
+    SubTable();
+    SubTable(const SubTable& that) = delete;
+    SubTable& operator=(const SubTable& that) = delete;
+    SubTable(SubTable&& that) noexcept;
+    SubTable& operator=(SubTable&& that) noexcept;
+    ~SubTable();
+
+    CoverageFormat coverage;
+    // DeltaGlyphID for format 1.
+    // Substitutes for format 2.
+    absl::variant<absl::monostate, int16_t, DataVector<uint16_t>> table_data;
   };
 
-  struct TRangeRecord {
-    TRangeRecord();
+  struct Lookup {
+    using SubTables = std::vector<SubTable>;
 
-    uint16_t Start = 0;
-    uint16_t End = 0;
-    uint16_t StartCoverageIndex = 0;
+    Lookup();
+    Lookup(const Lookup& that) = delete;
+    Lookup& operator=(const Lookup& that) = delete;
+    Lookup(Lookup&& that) noexcept;
+    Lookup& operator=(Lookup&& that) noexcept;
+    ~Lookup();
+
+    uint16_t lookup_type = 0;
+    SubTables sub_tables;
   };
 
-  struct TCoverageFormatBase {
-    explicit TCoverageFormatBase(uint16_t format) : CoverageFormat(format) {}
-    virtual ~TCoverageFormatBase() = default;
+  bool LoadGSUBTable(pdfium::span<const uint8_t> gsub);
+  void Parse(pdfium::span<const uint8_t> scriptlist,
+             pdfium::span<const uint8_t> featurelist,
+             pdfium::span<const uint8_t> lookuplist);
+  void ParseScriptList(pdfium::span<const uint8_t> raw);
+  ScriptRecord ParseScript(const uint8_t* raw);
+  FeatureIndices ParseLangSys(const uint8_t* raw);
+  void ParseFeatureList(pdfium::span<const uint8_t> raw);
+  DataVector<uint16_t> ParseFeatureLookupListIndices(const uint8_t* raw);
+  void ParseLookupList(pdfium::span<const uint8_t> raw);
+  Lookup ParseLookup(const uint8_t* raw);
+  CoverageFormat ParseCoverage(const uint8_t* raw);
+  SubTable ParseSingleSubst(const uint8_t* raw);
 
-    const uint16_t CoverageFormat;
-  };
-
-  struct TCoverageFormat1 final : public TCoverageFormatBase {
-    explicit TCoverageFormat1(size_t initial_size);
-    ~TCoverageFormat1() override;
-
-    DataVector<uint16_t> GlyphArray;
-  };
-
-  struct TCoverageFormat2 final : public TCoverageFormatBase {
-    explicit TCoverageFormat2(size_t initial_size);
-    ~TCoverageFormat2() override;
-
-    std::vector<TRangeRecord> RangeRecords;
-  };
-
-  struct TDevice {
-    TDevice();
-
-    uint16_t StartSize = 0;
-    uint16_t EndSize = 0;
-    uint16_t DeltaFormat = 0;
-  };
-
-  struct TSubTableBase {
-    explicit TSubTableBase(uint16_t format);
-    virtual ~TSubTableBase();
-
-    const uint16_t SubstFormat;
-    std::unique_ptr<TCoverageFormatBase> Coverage;
-  };
-
-  struct TSubTable1 final : public TSubTableBase {
-    TSubTable1();
-    ~TSubTable1() override;
-
-    int16_t DeltaGlyphID = 0;
-  };
-
-  struct TSubTable2 final : public TSubTableBase {
-    TSubTable2();
-    ~TSubTable2() override;
-
-    DataVector<uint16_t> Substitutes;
-  };
-
-  struct TLookup {
-    TLookup();
-    ~TLookup();
-
-    uint16_t LookupType = 0;
-    uint16_t LookupFlag = 0;
-    std::vector<std::unique_ptr<TSubTableBase>> SubTables;
-  };
-
-  bool LoadGSUBTable(FT_Bytes gsub);
-  bool Parse(FT_Bytes scriptlist, FT_Bytes featurelist, FT_Bytes lookuplist);
-  void ParseScriptList(FT_Bytes raw);
-  void ParseScript(FT_Bytes raw, TScriptRecord* rec);
-  void ParseLangSys(FT_Bytes raw, TLangSysRecord* rec);
-  void ParseFeatureList(FT_Bytes raw);
-  void ParseFeature(FT_Bytes raw, TFeatureRecord* rec);
-  void ParseLookupList(FT_Bytes raw);
-  void ParseLookup(FT_Bytes raw, TLookup* rec);
-  std::unique_ptr<TCoverageFormatBase> ParseCoverage(FT_Bytes raw);
-  std::unique_ptr<TCoverageFormat1> ParseCoverageFormat1(FT_Bytes raw);
-  std::unique_ptr<TCoverageFormat2> ParseCoverageFormat2(FT_Bytes raw);
-  std::unique_ptr<TSubTableBase> ParseSingleSubst(FT_Bytes raw);
-  std::unique_ptr<TSubTable1> ParseSingleSubstFormat1(FT_Bytes raw);
-  std::unique_ptr<TSubTable2> ParseSingleSubstFormat2(FT_Bytes raw);
-
-  absl::optional<uint32_t> GetVerticalGlyphSub(const TFeatureRecord& feature,
+  absl::optional<uint32_t> GetVerticalGlyphSub(const FeatureRecord& feature,
                                                uint32_t glyphnum) const;
-  absl::optional<uint32_t> GetVerticalGlyphSub2(const TLookup& lookup,
+  absl::optional<uint32_t> GetVerticalGlyphSub2(const Lookup& lookup,
                                                 uint32_t glyphnum) const;
-  int GetCoverageIndex(TCoverageFormatBase* Coverage, uint32_t g) const;
+  int GetCoverageIndex(const CoverageFormat& coverage, uint32_t g) const;
 
-  uint8_t GetUInt8(FT_Bytes& p) const;
-  int16_t GetInt16(FT_Bytes& p) const;
-  uint16_t GetUInt16(FT_Bytes& p) const;
-  int32_t GetInt32(FT_Bytes& p) const;
-  uint32_t GetUInt32(FT_Bytes& p) const;
+  uint8_t GetUInt8(const uint8_t*& p) const;
+  int16_t GetInt16(const uint8_t*& p) const;
+  uint16_t GetUInt16(const uint8_t*& p) const;
+  int32_t GetInt32(const uint8_t*& p) const;
+  uint32_t GetUInt32(const uint8_t*& p) const;
 
-  std::set<uint32_t> m_featureSet;
-  std::vector<TScriptRecord> ScriptList;
-  std::vector<TFeatureRecord> FeatureList;
-  std::vector<TLookup> LookupList;
+  std::set<uint32_t> feature_set_;
+  std::vector<ScriptRecord> script_list_;
+  std::vector<FeatureRecord> feature_list_;
+  std::vector<Lookup> lookup_list_;
 };
 
 #endif  // CORE_FPDFAPI_FONT_CFX_CTTGSUBTABLE_H_
diff --git a/core/fpdfapi/font/cfx_stockfontarray.cpp b/core/fpdfapi/font/cfx_stockfontarray.cpp
index b6793ff..3142525 100644
--- a/core/fpdfapi/font/cfx_stockfontarray.cpp
+++ b/core/fpdfapi/font/cfx_stockfontarray.cpp
@@ -11,7 +11,7 @@
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
-#include "third_party/base/notreached.h"
+#include "third_party/base/check_op.h"
 
 CFX_StockFontArray::CFX_StockFontArray() = default;
 
@@ -29,10 +29,8 @@
 
 RetainPtr<CPDF_Font> CFX_StockFontArray::GetFont(
     CFX_FontMapper::StandardFont index) const {
-  if (index < std::size(m_StockFonts))
-    return m_StockFonts[index];
-  NOTREACHED();
-  return nullptr;
+  CHECK_LT(index, std::size(m_StockFonts));
+  return m_StockFonts[index];
 }
 
 void CFX_StockFontArray::SetFont(CFX_FontMapper::StandardFont index,
diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.h b/core/fpdfapi/font/cpdf_cid2unicodemap.h
index 5c18493..6bc5c12 100644
--- a/core/fpdfapi/font/cpdf_cid2unicodemap.h
+++ b/core/fpdfapi/font/cpdf_cid2unicodemap.h
@@ -8,7 +8,7 @@
 #define CORE_FPDFAPI_FONT_CPDF_CID2UNICODEMAP_H_
 
 #include "core/fpdfapi/font/cpdf_cidfont.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_CID2UnicodeMap {
  public:
diff --git a/core/fpdfapi/font/cpdf_cidfont.cpp b/core/fpdfapi/font/cpdf_cidfont.cpp
index 6e0e665..7d2d549 100644
--- a/core/fpdfapi/font/cpdf_cidfont.cpp
+++ b/core/fpdfapi/font/cpdf_cidfont.cpp
@@ -23,6 +23,7 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcrt/fixed_uninit_data_vector.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_safe_types.h"
@@ -31,8 +32,7 @@
 #include "core/fxge/fx_font.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
@@ -141,7 +141,7 @@
   // Boundary values to avoid integer overflow when multiplied by 1000.
   constexpr FT_Pos kMinCBox = -2147483;
   constexpr FT_Pos kMaxCBox = 2147483;
-  return static_cast<int>(pdfium::clamp(pos, kMinCBox, kMaxCBox));
+  return static_cast<int>(std::clamp(pos, kMinCBox, kMaxCBox));
 }
 
 #if !BUILDFLAG(IS_WIN)
@@ -660,18 +660,22 @@
 
   static constexpr uint32_t kGsubTag =
       CFX_FontMapper::MakeTag('G', 'S', 'U', 'B');
-  if (!m_Font.GetSubData()) {
-    unsigned long length = 0;
-    int error = FT_Load_Sfnt_Table(face, kGsubTag, 0, nullptr, &length);
-    if (!error)
-      m_Font.AllocSubData(length);
-  }
-  int error =
-      FT_Load_Sfnt_Table(face, kGsubTag, 0, m_Font.GetSubData(), nullptr);
-  if (error || !m_Font.GetSubData())
+  unsigned long length = 0;
+  int error = FT_Load_Sfnt_Table(face, kGsubTag, 0, nullptr, &length);
+  if (error || !length) {
     return index;
+  }
 
-  m_pTTGSUBTable = std::make_unique<CFX_CTTGSUBTable>(m_Font.GetSubData());
+  FixedUninitDataVector<uint8_t> sub_data(length);
+  error = FT_Load_Sfnt_Table(face, kGsubTag, 0, sub_data.writable_span().data(),
+                             nullptr);
+  if (error) {
+    return index;
+  }
+
+  // CFX_CTTGSUBTable parses the data and stores all the values in its structs.
+  // It does not store pointers into `sub_data`.
+  m_pTTGSUBTable = std::make_unique<CFX_CTTGSUBTable>(sub_data.span());
   return GetVerticalGlyph(index, pVertGlyph);
 }
 
@@ -883,10 +887,12 @@
   if (m_Charset != CIDSET_JAPAN1 || m_pFontFile)
     return nullptr;
 
-  const auto* pEnd = kJapan1VerticalCIDs + std::size(kJapan1VerticalCIDs);
+  const auto* pBegin = std::begin(kJapan1VerticalCIDs);
+  const auto* pEnd = std::end(kJapan1VerticalCIDs);
   const auto* pTransform = std::lower_bound(
-      kJapan1VerticalCIDs, pEnd, cid,
+      pBegin, pEnd, cid,
       [](const CIDTransform& entry, uint16_t cid) { return entry.cid < cid; });
+
   return (pTransform < pEnd && cid == pTransform->cid) ? &pTransform->a
                                                        : nullptr;
 }
diff --git a/core/fpdfapi/font/cpdf_cmap.h b/core/fpdfapi/font/cpdf_cmap.h
index cc186e8..416010a 100644
--- a/core/fpdfapi/font/cpdf_cmap.h
+++ b/core/fpdfapi/font/cpdf_cmap.h
@@ -15,7 +15,7 @@
 #include "core/fxcrt/fixed_zeroed_data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcmap {
 struct CMap;
diff --git a/core/fpdfapi/font/cpdf_font.h b/core/fpdfapi/font/cpdf_font.h
index a74cba9..e192a5c 100644
--- a/core/fpdfapi/font/cpdf_font.h
+++ b/core/fpdfapi/font/cpdf_font.h
@@ -68,8 +68,6 @@
   static RetainPtr<CPDF_Font> GetStockFont(CPDF_Document* pDoc,
                                            ByteStringView fontname);
 
-  ~CPDF_Font() override;
-
   virtual bool IsType1Font() const;
   virtual bool IsTrueTypeFont() const;
   virtual bool IsType3Font() const;
@@ -138,6 +136,7 @@
 
  protected:
   CPDF_Font(CPDF_Document* pDocument, RetainPtr<CPDF_Dictionary> pFontDict);
+  ~CPDF_Font() override;
 
   static int TT2PDF(FT_Pos m, FXFT_FaceRec* face);
 
diff --git a/core/fpdfapi/font/cpdf_fontglobals.cpp b/core/fpdfapi/font/cpdf_fontglobals.cpp
index 1aa19d6..aa5386d 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.cpp
+++ b/core/fpdfapi/font/cpdf_fontglobals.cpp
@@ -50,10 +50,7 @@
   return g_FontGlobals;
 }
 
-CPDF_FontGlobals::CPDF_FontGlobals() {
-  memset(m_EmbeddedCharsets, 0, sizeof(m_EmbeddedCharsets));
-  memset(m_EmbeddedToUnicodes, 0, sizeof(m_EmbeddedToUnicodes));
-}
+CPDF_FontGlobals::CPDF_FontGlobals() = default;
 
 CPDF_FontGlobals::~CPDF_FontGlobals() = default;
 
diff --git a/core/fpdfapi/font/cpdf_fontglobals.h b/core/fpdfapi/font/cpdf_fontglobals.h
index 6b2e3bc..f5148a0 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.h
+++ b/core/fpdfapi/font/cpdf_fontglobals.h
@@ -7,6 +7,7 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
 #define CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
 
+#include <array>
 #include <functional>
 #include <map>
 #include <memory>
@@ -15,7 +16,7 @@
 #include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_StockFontArray;
 class CPDF_Font;
@@ -63,9 +64,12 @@
   void LoadEmbeddedKorea1CMaps();
 
   std::map<ByteString, RetainPtr<const CPDF_CMap>> m_CMaps;
-  std::unique_ptr<CPDF_CID2UnicodeMap> m_CID2UnicodeMaps[CIDSET_NUM_SETS];
-  pdfium::span<const fxcmap::CMap> m_EmbeddedCharsets[CIDSET_NUM_SETS];
-  pdfium::span<const uint16_t> m_EmbeddedToUnicodes[CIDSET_NUM_SETS];
+  std::array<std::unique_ptr<CPDF_CID2UnicodeMap>, CIDSET_NUM_SETS>
+      m_CID2UnicodeMaps;
+  std::array<pdfium::span<const fxcmap::CMap>, CIDSET_NUM_SETS>
+      m_EmbeddedCharsets;
+  std::array<pdfium::span<const uint16_t>, CIDSET_NUM_SETS>
+      m_EmbeddedToUnicodes;
   std::map<UnownedPtr<CPDF_Document>,
            std::unique_ptr<CFX_StockFontArray>,
            std::less<>>
diff --git a/core/fpdfapi/font/cpdf_tounicodemap.cpp b/core/fpdfapi/font/cpdf_tounicodemap.cpp
index 3c6e73e..2e354a2 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap.cpp
@@ -13,6 +13,7 @@
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/containers/contains.h"
@@ -78,7 +79,28 @@
 }
 
 // static
-absl::optional<uint32_t> CPDF_ToUnicodeMap::StringToCode(ByteStringView str) {
+absl::optional<uint32_t> CPDF_ToUnicodeMap::StringToCode(ByteStringView input) {
+  // Ignore whitespaces within `input`. See https://crbug.com/pdfium/2065.
+  std::set<char> seen_whitespace_chars;
+  for (char c : input) {
+    if (PDFCharIsWhitespace(c)) {
+      seen_whitespace_chars.insert(c);
+    }
+  }
+  ByteString str_without_whitespace_chars;  // Must outlive `str`.
+  ByteStringView str;
+  if (seen_whitespace_chars.empty()) {
+    str = input;
+  } else {
+    str_without_whitespace_chars.Reserve(input.GetLength());
+    for (char c : input) {
+      if (!pdfium::Contains(seen_whitespace_chars, c)) {
+        str_without_whitespace_chars += c;
+      }
+    }
+    str = str_without_whitespace_chars.AsStringView();
+  }
+
   size_t len = str.GetLength();
   if (len <= 2 || str[0] != '<' || str[len - 1] != '>')
     return absl::nullopt;
diff --git a/core/fpdfapi/font/cpdf_tounicodemap.h b/core/fpdfapi/font/cpdf_tounicodemap.h
index a074f45..7f4ba03 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.h
+++ b/core/fpdfapi/font/cpdf_tounicodemap.h
@@ -34,7 +34,7 @@
   friend class cpdf_tounicodemap_StringToCode_Test;
   friend class cpdf_tounicodemap_StringToWideString_Test;
 
-  static absl::optional<uint32_t> StringToCode(ByteStringView str);
+  static absl::optional<uint32_t> StringToCode(ByteStringView input);
   static WideString StringToWideString(ByteStringView str);
 
   void Load(RetainPtr<const CPDF_Stream> pStream);
diff --git a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
index 86ac2b1..7a3015d 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
@@ -8,7 +8,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(cpdf_tounicodemap, StringToCode) {
   EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<0001>"), testing::Optional(1u));
@@ -19,6 +19,14 @@
   EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<FFFFFFFF>"),
               testing::Optional(4294967295u));
 
+  // Whitespaces within the string are ignored.
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<00\n0\r1>"),
+              testing::Optional(1u));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<c 2>"),
+              testing::Optional(194u));
+  EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<A2\r\n>"),
+              testing::Optional(162u));
+
   // Integer overflow
   EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<100000000>").has_value());
   EXPECT_FALSE(CPDF_ToUnicodeMap::StringToCode("<1abcdFFFF>").has_value());
diff --git a/core/fpdfapi/font/cpdf_truetypefont.cpp b/core/fpdfapi/font/cpdf_truetypefont.cpp
index 0a59b54..f19ace9 100644
--- a/core/fpdfapi/font/cpdf_truetypefont.cpp
+++ b/core/fpdfapi/font/cpdf_truetypefont.cpp
@@ -11,7 +11,6 @@
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
diff --git a/core/fpdfapi/font/cpdf_type3char.h b/core/fpdfapi/font/cpdf_type3char.h
index afa39ef..990fb77 100644
--- a/core/fpdfapi/font/cpdf_type3char.h
+++ b/core/fpdfapi/font/cpdf_type3char.h
@@ -14,7 +14,7 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBitmap;
 
diff --git a/core/fpdfapi/page/BUILD.gn b/core/fpdfapi/page/BUILD.gn
index 51b4adf..f6bbd74 100644
--- a/core/fpdfapi/page/BUILD.gn
+++ b/core/fpdfapi/page/BUILD.gn
@@ -108,13 +108,15 @@
     "ipdf_page.h",
   ]
   configs += [ "../../../:pdfium_strict_config" ]
+  public_deps = [
+    "../../fxge",
+    "../parser",
+  ]
   deps = [
     "../../../constants",
     "../../fxcodec",
     "../../fxcrt",
-    "../../fxge",
     "../font",
-    "../parser",
   ]
   allow_circular_includes_from = []
   if (pdf_use_skia) {
diff --git a/core/fpdfapi/page/DEPS b/core/fpdfapi/page/DEPS
new file mode 100644
index 0000000..3dc6946
--- /dev/null
+++ b/core/fpdfapi/page/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  'cpdf_pageimagecache\.cpp': [
+    '+third_party/skia/include',
+  ],
+}
diff --git a/core/fpdfapi/page/cpdf_allstates.cpp b/core/fpdfapi/page/cpdf_allstates.cpp
index a4330db..996b3f79 100644
--- a/core/fpdfapi/page/cpdf_allstates.cpp
+++ b/core/fpdfapi/page/cpdf_allstates.cpp
@@ -18,7 +18,6 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/bytestring.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "third_party/base/cxx17_backports.h"
 
 CPDF_AllStates::CPDF_AllStates() = default;
 
@@ -26,7 +25,7 @@
 
 void CPDF_AllStates::Copy(const CPDF_AllStates& src) {
   CopyStates(src);
-  m_GraphicsResourceName = src.m_GraphicsResourceName;
+  m_GraphicsResourceNames = src.m_GraphicsResourceNames;
   m_TextMatrix = src.m_TextMatrix;
   m_ParentMatrix = src.m_ParentMatrix;
   m_CTM = src.m_CTM;
@@ -117,11 +116,11 @@
       }
       case FXBSTR_ID('C', 'A', 0, 0):
         m_GeneralState.SetStrokeAlpha(
-            pdfium::clamp(pObject->GetNumber(), 0.0f, 1.0f));
+            std::clamp(pObject->GetNumber(), 0.0f, 1.0f));
         break;
       case FXBSTR_ID('c', 'a', 0, 0):
         m_GeneralState.SetFillAlpha(
-            pdfium::clamp(pObject->GetNumber(), 0.0f, 1.0f));
+            std::clamp(pObject->GetNumber(), 0.0f, 1.0f));
         break;
       case FXBSTR_ID('O', 'P', 0, 0):
         m_GeneralState.SetStrokeOP(!!pObject->GetInteger());
diff --git a/core/fpdfapi/page/cpdf_allstates.h b/core/fpdfapi/page/cpdf_allstates.h
index 63eb527..3d4d9a3 100644
--- a/core/fpdfapi/page/cpdf_allstates.h
+++ b/core/fpdfapi/page/cpdf_allstates.h
@@ -7,6 +7,8 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_ALLSTATES_H_
 #define CORE_FPDFAPI_PAGE_CPDF_ALLSTATES_H_
 
+#include <vector>
+
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
@@ -25,7 +27,7 @@
                     CPDF_StreamContentParser* pParser);
   void SetLineDash(const CPDF_Array* pArray, float phase, float scale);
 
-  ByteString m_GraphicsResourceName;
+  std::vector<ByteString> m_GraphicsResourceNames;
   CFX_Matrix m_TextMatrix;
   CFX_Matrix m_CTM;
   CFX_Matrix m_ParentMatrix;
diff --git a/core/fpdfapi/page/cpdf_color.h b/core/fpdfapi/page/cpdf_color.h
index d3e605b..a2ba7cb 100644
--- a/core/fpdfapi/page/cpdf_color.h
+++ b/core/fpdfapi/page/cpdf_color.h
@@ -13,7 +13,7 @@
 #include <vector>
 
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_ColorSpace;
 class CPDF_Pattern;
diff --git a/core/fpdfapi/page/cpdf_colorspace.cpp b/core/fpdfapi/page/cpdf_colorspace.cpp
index 9d94acd..2a9d2f4 100644
--- a/core/fpdfapi/page/cpdf_colorspace.cpp
+++ b/core/fpdfapi/page/cpdf_colorspace.cpp
@@ -43,7 +43,6 @@
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/notreached.h"
 
 namespace {
@@ -373,7 +372,7 @@
 };
 
 float RGB_Conversion(float colorComponent) {
-  colorComponent = pdfium::clamp(colorComponent, 0.0f, 1.0f);
+  colorComponent = std::clamp(colorComponent, 0.0f, 1.0f);
   int scale = std::max(static_cast<int>(colorComponent * 1023), 0);
   if (scale < 192)
     return kSRGBSamples1[scale] / 255.0f;
@@ -794,7 +793,7 @@
     if (range_min <= range_max) {
       *min = range_min;
       *max = range_max;
-      *value = pdfium::clamp(0.0f, *min, *max);
+      *value = std::clamp(0.0f, *min, *max);
       return;
     }
   }
diff --git a/core/fpdfapi/page/cpdf_colorspace.h b/core/fpdfapi/page/cpdf_colorspace.h
index 23253c8..39f84ec 100644
--- a/core/fpdfapi/page/cpdf_colorspace.h
+++ b/core/fpdfapi/page/cpdf_colorspace.h
@@ -22,7 +22,7 @@
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Document;
 class CPDF_IndexedCS;
diff --git a/core/fpdfapi/page/cpdf_colorstate.h b/core/fpdfapi/page/cpdf_colorstate.h
index ec2de6b..6ddae8a 100644
--- a/core/fpdfapi/page/cpdf_colorstate.h
+++ b/core/fpdfapi/page/cpdf_colorstate.h
@@ -13,7 +13,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_ColorSpace;
 class CPDF_Pattern;
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.cpp b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
index 6b5cd62..f68742b 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.cpp
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
@@ -22,7 +22,6 @@
     case kDirectDict:
       return m_pDirectDict;
     case kNone:
-    default:
       return nullptr;
   }
 }
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.h b/core/fpdfapi/page/cpdf_contentmarkitem.h
index f418aab..e3a041d 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.h
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.h
@@ -16,8 +16,7 @@
  public:
   enum ParamType { kNone, kPropertiesDict, kDirectDict };
 
-  explicit CPDF_ContentMarkItem(ByteString name);
-  ~CPDF_ContentMarkItem() override;
+  CONSTRUCT_VIA_MAKE_RETAIN;
 
   const ByteString& GetName() const { return m_MarkName; }
   ParamType GetParamType() const { return m_ParamType; }
@@ -30,6 +29,9 @@
                            const ByteString& property_name);
 
  private:
+  explicit CPDF_ContentMarkItem(ByteString name);
+  ~CPDF_ContentMarkItem() override;
+
   ParamType m_ParamType = kNone;
   ByteString m_MarkName;
   ByteString m_PropertyName;
diff --git a/core/fpdfapi/page/cpdf_contentmarks.h b/core/fpdfapi/page/cpdf_contentmarks.h
index 95eaeea..ef3ebd6 100644
--- a/core/fpdfapi/page/cpdf_contentmarks.h
+++ b/core/fpdfapi/page/cpdf_contentmarks.h
@@ -42,9 +42,7 @@
  private:
   class MarkData final : public Retainable {
    public:
-    MarkData();
-    MarkData(const MarkData& src);
-    ~MarkData() override;
+    CONSTRUCT_VIA_MAKE_RETAIN;
 
     size_t CountItems() const;
     bool ContainsItem(const CPDF_ContentMarkItem* pItem) const;
@@ -61,6 +59,10 @@
     bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
 
    private:
+    MarkData();
+    MarkData(const MarkData& src);
+    ~MarkData() override;
+
     std::vector<RetainPtr<CPDF_ContentMarkItem>> m_Marks;
   };
 
diff --git a/core/fpdfapi/page/cpdf_contentparser.cpp b/core/fpdfapi/page/cpdf_contentparser.cpp
index 5ef9d80..1fbcb0b 100644
--- a/core/fpdfapi/page/cpdf_contentparser.cpp
+++ b/core/fpdfapi/page/cpdf_contentparser.cpp
@@ -56,12 +56,13 @@
   HandlePageContentFailure();
 }
 
-CPDF_ContentParser::CPDF_ContentParser(RetainPtr<const CPDF_Stream> pStream,
-                                       CPDF_PageObjectHolder* pPageObjectHolder,
-                                       const CPDF_AllStates* pGraphicStates,
-                                       const CFX_Matrix* pParentMatrix,
-                                       CPDF_Type3Char* pType3Char,
-                                       std::set<const uint8_t*>* pParsedSet)
+CPDF_ContentParser::CPDF_ContentParser(
+    RetainPtr<const CPDF_Stream> pStream,
+    CPDF_PageObjectHolder* pPageObjectHolder,
+    const CPDF_AllStates* pGraphicStates,
+    const CFX_Matrix* pParentMatrix,
+    CPDF_Type3Char* pType3Char,
+    CPDF_Form::RecursionState* recursion_state)
     : m_CurrentStage(Stage::kParse),
       m_pPageObjectHolder(pPageObjectHolder),
       m_pType3Char(pType3Char) {
@@ -95,7 +96,7 @@
       m_pPageObjectHolder->GetMutablePageResources(),
       m_pPageObjectHolder->GetMutableResources(), pParentMatrix,
       m_pPageObjectHolder, std::move(pResources), form_bbox, pGraphicStates,
-      pParsedSet);
+      recursion_state);
   m_pParser->GetCurStates()->m_CTM = form_matrix;
   m_pParser->GetCurStates()->m_ParentMatrix = form_matrix;
   if (ClipPath.HasRef()) {
@@ -197,12 +198,13 @@
 
 CPDF_ContentParser::Stage CPDF_ContentParser::Parse() {
   if (!m_pParser) {
-    m_ParsedSet.clear();
+    m_RecursionState.parsed_set.clear();
+    m_RecursionState.form_count = 0;
     m_pParser = std::make_unique<CPDF_StreamContentParser>(
         m_pPageObjectHolder->GetDocument(),
         m_pPageObjectHolder->GetMutablePageResources(), nullptr, nullptr,
         m_pPageObjectHolder, m_pPageObjectHolder->GetMutableResources(),
-        m_pPageObjectHolder->GetBBox(), nullptr, &m_ParsedSet);
+        m_pPageObjectHolder->GetBBox(), nullptr, &m_RecursionState);
     m_pParser->GetCurStates()->m_ColorState.SetDefault();
   }
   if (m_CurrentOffset >= GetData().size())
diff --git a/core/fpdfapi/page/cpdf_contentparser.h b/core/fpdfapi/page/cpdf_contentparser.h
index 090c624..e2660e2 100644
--- a/core/fpdfapi/page/cpdf_contentparser.h
+++ b/core/fpdfapi/page/cpdf_contentparser.h
@@ -10,9 +10,9 @@
 #include <stdint.h>
 
 #include <memory>
-#include <set>
 #include <vector>
 
+#include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_streamcontentparser.h"
 #include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
@@ -36,7 +36,7 @@
                      const CPDF_AllStates* pGraphicStates,
                      const CFX_Matrix* pParentMatrix,
                      CPDF_Type3Char* pType3Char,
-                     std::set<const uint8_t*>* pParsedSet);
+                     CPDF_Form::RecursionState* recursion_state);
   ~CPDF_ContentParser();
 
   const CPDF_AllStates* GetCurStates() const {
@@ -80,9 +80,10 @@
       m_Data;
   uint32_t m_nStreams = 0;
   uint32_t m_CurrentOffset = 0;
-  std::set<const uint8_t*> m_ParsedSet;  // Only used when parsing pages.
+  // Only used when parsing pages.
+  CPDF_Form::RecursionState m_RecursionState;
 
-  // Must not outlive |m_pParsedSet|.
+  // Must not outlive |m_RecursionState|.
   std::unique_ptr<CPDF_StreamContentParser> m_pParser;
 };
 
diff --git a/core/fpdfapi/page/cpdf_devicecs.cpp b/core/fpdfapi/page/cpdf_devicecs.cpp
index 5590c5b..0faabce 100644
--- a/core/fpdfapi/page/cpdf_devicecs.cpp
+++ b/core/fpdfapi/page/cpdf_devicecs.cpp
@@ -16,13 +16,12 @@
 #include "core/fxcodec/fx_codec.h"
 #include "core/fxge/dib/cfx_cmyk_to_srgb.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/notreached.h"
 
 namespace {
 
 float NormalizeChannel(float fVal) {
-  return pdfium::clamp(fVal, 0.0f, 1.0f);
+  return std::clamp(fVal, 0.0f, 1.0f);
 }
 
 }  // namespace
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp
index 29db5a4..30f1dcf 100644
--- a/core/fpdfapi/page/cpdf_dib.cpp
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -39,8 +39,6 @@
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
-#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -51,7 +49,7 @@
 
 unsigned int GetBits8(const uint8_t* pData, uint64_t bitpos, size_t nbits) {
   DCHECK(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16);
-  DCHECK_EQ((bitpos & (nbits - 1)), 0);
+  DCHECK_EQ((bitpos & (nbits - 1)), 0u);
   unsigned int byte = pData[bitpos / 8];
   if (nbits == 8)
     return byte;
@@ -180,9 +178,6 @@
 
     case OPJ_CLRSPC_CMYK:
       return JpxDecodeAction::kUseCmyk;
-
-    default:
-      NOTREACHED_NORETURN();
   }
 }
 
@@ -356,7 +351,8 @@
     iDecodeStatus = Jbig2Decoder::StartDecode(
         m_pJbig2Context.get(), m_pDocument->GetOrCreateCodecContext(), m_Width,
         m_Height, pSrcSpan, nSrcKey, pGlobalSpan, nGlobalKey,
-        m_pCachedBitmap->GetBuffer(), m_pCachedBitmap->GetPitch(), pPause);
+        m_pCachedBitmap->GetWritableBuffer(), m_pCachedBitmap->GetPitch(),
+        pPause);
   } else {
     iDecodeStatus = Jbig2Decoder::ContinueDecode(m_pJbig2Context.get(), pPause);
   }
@@ -690,9 +686,9 @@
   // If |original_colorspace| exists, then LoadColorInfo() already set
   // |m_nComponents|.
   if (original_colorspace) {
-    DCHECK_NE(0, m_nComponents);
+    DCHECK_NE(0u, m_nComponents);
   } else {
-    DCHECK_EQ(0, m_nComponents);
+    DCHECK_EQ(0u, m_nComponents);
     m_nComponents = GetComponentCountFromOpjColorSpace(image_info.colorspace);
     if (m_nComponents == 0) {
       return nullptr;
@@ -719,13 +715,13 @@
     return nullptr;
 
   result_bitmap->Clear(0xFFFFFFFF);
-  if (!decoder->Decode(result_bitmap->GetBuffer(), result_bitmap->GetPitch(),
-                       swap_rgb, m_nComponents)) {
+  if (!decoder->Decode(result_bitmap->GetWritableBuffer(),
+                       result_bitmap->GetPitch(), swap_rgb, m_nComponents)) {
     return nullptr;
   }
 
   if (convert_argb_to_rgb) {
-    DCHECK_EQ(3, m_nComponents);
+    DCHECK_EQ(3u, m_nComponents);
     auto rgb_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
     if (!rgb_bitmap->Create(image_info.width, image_info.height,
                             FXDIB_Format::kRgb)) {
@@ -1061,9 +1057,9 @@
     } else if (m_Family != CPDF_ColorSpace::Family::kPattern) {
       m_pColorSpace->GetRGB(color_values, &R, &G, &B);
     }
-    R = pdfium::clamp(R, 0.0f, 1.0f);
-    G = pdfium::clamp(G, 0.0f, 1.0f);
-    B = pdfium::clamp(B, 0.0f, 1.0f);
+    R = std::clamp(R, 0.0f, 1.0f);
+    G = std::clamp(G, 0.0f, 1.0f);
+    B = std::clamp(B, 0.0f, 1.0f);
     dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
     dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
     dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
@@ -1135,9 +1131,9 @@
   return true;
 }
 
-pdfium::span<uint8_t> CPDF_DIB::GetBuffer() const {
+pdfium::span<const uint8_t> CPDF_DIB::GetBuffer() const {
   return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer()
-                         : pdfium::span<uint8_t>();
+                         : pdfium::span<const uint8_t>();
 }
 
 pdfium::span<const uint8_t> CPDF_DIB::GetScanline(int line) const {
diff --git a/core/fpdfapi/page/cpdf_dib.h b/core/fpdfapi/page/cpdf_dib.h
index a24d192..5206bf5 100644
--- a/core/fpdfapi/page/cpdf_dib.h
+++ b/core/fpdfapi/page/cpdf_dib.h
@@ -17,7 +17,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/cfx_dibbase.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
@@ -45,7 +45,7 @@
   CONSTRUCT_VIA_MAKE_RETAIN;
 
   // CFX_DIBBase:
-  pdfium::span<uint8_t> GetBuffer() const override;
+  pdfium::span<const uint8_t> GetBuffer() const override;
   pdfium::span<const uint8_t> GetScanline(int line) const override;
   bool SkipToScanline(int line, PauseIndicatorIface* pPause) const override;
   size_t GetEstimatedImageMemoryBurden() const override;
diff --git a/core/fpdfapi/page/cpdf_expintfunc.h b/core/fpdfapi/page/cpdf_expintfunc.h
index 08b12fd..22e7f12 100644
--- a/core/fpdfapi/page/cpdf_expintfunc.h
+++ b/core/fpdfapi/page/cpdf_expintfunc.h
@@ -11,7 +11,7 @@
 #include "core/fxcrt/data_vector.h"
 
 #if defined(_SKIA_SUPPORT_)
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #endif
 
 class CPDF_ExpIntFunc final : public CPDF_Function {
diff --git a/core/fpdfapi/page/cpdf_form.cpp b/core/fpdfapi/page/cpdf_form.cpp
index 90a8650..e66ff0b 100644
--- a/core/fpdfapi/page/cpdf_form.cpp
+++ b/core/fpdfapi/page/cpdf_form.cpp
@@ -18,6 +18,10 @@
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "third_party/base/check_op.h"
 
+CPDF_Form::RecursionState::RecursionState() = default;
+
+CPDF_Form::RecursionState::~RecursionState() = default;
+
 // static
 CPDF_Dictionary* CPDF_Form::ChooseResourcesDict(
     CPDF_Dictionary* pResources,
@@ -61,8 +65,8 @@
 
 void CPDF_Form::ParseContent(const CPDF_AllStates* pGraphicStates,
                              const CFX_Matrix* pParentMatrix,
-                             std::set<const uint8_t*>* pParsedSet) {
-  ParseContentInternal(pGraphicStates, pParentMatrix, nullptr, pParsedSet);
+                             RecursionState* recursion_state) {
+  ParseContentInternal(pGraphicStates, pParentMatrix, nullptr, recursion_state);
 }
 
 void CPDF_Form::ParseContentForType3Char(CPDF_Type3Char* pType3Char) {
@@ -72,14 +76,14 @@
 void CPDF_Form::ParseContentInternal(const CPDF_AllStates* pGraphicStates,
                                      const CFX_Matrix* pParentMatrix,
                                      CPDF_Type3Char* pType3Char,
-                                     std::set<const uint8_t*>* pParsedSet) {
+                                     RecursionState* recursion_state) {
   if (GetParseState() == ParseState::kParsed)
     return;
 
   if (GetParseState() == ParseState::kNotParsed) {
     StartParse(std::make_unique<CPDF_ContentParser>(
         GetStream(), this, pGraphicStates, pParentMatrix, pType3Char,
-        pParsedSet ? pParsedSet : &m_ParsedSet));
+        recursion_state ? recursion_state : &m_RecursionState));
   }
   DCHECK_EQ(GetParseState(), ParseState::kParsing);
   ContinueParse(nullptr);
diff --git a/core/fpdfapi/page/cpdf_form.h b/core/fpdfapi/page/cpdf_form.h
index 9052b9d..ba0fbd4 100644
--- a/core/fpdfapi/page/cpdf_form.h
+++ b/core/fpdfapi/page/cpdf_form.h
@@ -24,6 +24,14 @@
 class CPDF_Form final : public CPDF_PageObjectHolder,
                         public CPDF_Font::FormIface {
  public:
+  struct RecursionState {
+    RecursionState();
+    ~RecursionState();
+
+    std::set<const uint8_t*> parsed_set;
+    int form_count = 0;
+  };
+
   // Helper method to choose the first non-null resources dictionary.
   static CPDF_Dictionary* ChooseResourcesDict(CPDF_Dictionary* pResources,
                                               CPDF_Dictionary* pParentResources,
@@ -48,7 +56,7 @@
   void ParseContent();
   void ParseContent(const CPDF_AllStates* pGraphicStates,
                     const CFX_Matrix* pParentMatrix,
-                    std::set<const uint8_t*>* pParsedSet);
+                    RecursionState* recursion_state);
 
   RetainPtr<const CPDF_Stream> GetStream() const;
 
@@ -56,9 +64,9 @@
   void ParseContentInternal(const CPDF_AllStates* pGraphicStates,
                             const CFX_Matrix* pParentMatrix,
                             CPDF_Type3Char* pType3Char,
-                            std::set<const uint8_t*>* pParsedSet);
+                            RecursionState* recursion_state);
 
-  std::set<const uint8_t*> m_ParsedSet;
+  RecursionState m_RecursionState;
   RetainPtr<CPDF_Stream> const m_pFormStream;
 };
 
diff --git a/core/fpdfapi/page/cpdf_function.cpp b/core/fpdfapi/page/cpdf_function.cpp
index 85b9e41..5e885e1 100644
--- a/core/fpdfapi/page/cpdf_function.cpp
+++ b/core/fpdfapi/page/cpdf_function.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_function.h"
 
+#include <algorithm>
 #include <utility>
 #include <vector>
 
@@ -21,7 +22,6 @@
 #include "core/fxcrt/scoped_set_insertion.h"
 #include "core/fxcrt/stl_util.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -141,7 +141,7 @@
     if (domain1 > domain2)
       return absl::nullopt;
 
-    clamped_inputs[i] = pdfium::clamp(inputs[i], domain1, domain2);
+    clamped_inputs[i] = std::clamp(inputs[i], domain1, domain2);
   }
   if (!v_Call(clamped_inputs, results))
     return absl::nullopt;
@@ -155,7 +155,7 @@
     if (range1 > range2)
       return absl::nullopt;
 
-    results[i] = pdfium::clamp(results[i], range1, range2);
+    results[i] = std::clamp(results[i], range1, range2);
   }
   return m_nOutputs;
 }
diff --git a/core/fpdfapi/page/cpdf_function.h b/core/fpdfapi/page/cpdf_function.h
index faddb4e..d943e60 100644
--- a/core/fpdfapi/page/cpdf_function.h
+++ b/core/fpdfapi/page/cpdf_function.h
@@ -13,7 +13,7 @@
 
 #include "core/fxcrt/retain_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_ExpIntFunc;
 class CPDF_Object;
diff --git a/core/fpdfapi/page/cpdf_iccprofile.h b/core/fpdfapi/page/cpdf_iccprofile.h
index 0e040aa..3dd9f14 100644
--- a/core/fpdfapi/page/cpdf_iccprofile.h
+++ b/core/fpdfapi/page/cpdf_iccprofile.h
@@ -13,7 +13,7 @@
 
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Stream;
 
diff --git a/core/fpdfapi/page/cpdf_image.cpp b/core/fpdfapi/page/cpdf_image.cpp
index c9be231..6b70819 100644
--- a/core/fpdfapi/page/cpdf_image.cpp
+++ b/core/fpdfapi/page/cpdf_image.cpp
@@ -66,8 +66,8 @@
 CPDF_Image::~CPDF_Image() = default;
 
 void CPDF_Image::FinishInitialization() {
-  RetainPtr<CPDF_Dictionary> pStreamDict = m_pStream->GetMutableDict();
-  m_pOC = pStreamDict->GetMutableDictFor("OC");
+  RetainPtr<const CPDF_Dictionary> pStreamDict = m_pStream->GetDict();
+  m_pOC = pStreamDict->GetDictFor("OC");
   m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
               pStreamDict->GetBooleanFor("ImageMask", /*bDefault=*/false);
   m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
diff --git a/core/fpdfapi/page/cpdf_image.h b/core/fpdfapi/page/cpdf_image.h
index 04355a9..35357fc 100644
--- a/core/fpdfapi/page/cpdf_image.h
+++ b/core/fpdfapi/page/cpdf_image.h
@@ -12,7 +12,7 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
@@ -36,6 +36,8 @@
   RetainPtr<const CPDF_Dictionary> GetDict() const;
   RetainPtr<const CPDF_Stream> GetStream() const;
   RetainPtr<const CPDF_Dictionary> GetOC() const;
+
+  // Never returns nullptr.
   CPDF_Document* GetDocument() const { return m_pDocument; }
 
   int32_t GetPixelHeight() const { return m_Height; }
diff --git a/core/fpdfapi/page/cpdf_imageobject.cpp b/core/fpdfapi/page/cpdf_imageobject.cpp
index c031502..0511a02 100644
--- a/core/fpdfapi/page/cpdf_imageobject.cpp
+++ b/core/fpdfapi/page/cpdf_imageobject.cpp
@@ -82,10 +82,6 @@
   if (!m_pImage)
     return;
 
-  auto* pDoc = m_pImage->GetDocument();
-  if (!pDoc)
-    return;
-
   RetainPtr<const CPDF_Stream> pStream = m_pImage->GetStream();
   if (!pStream)
     return;
@@ -94,6 +90,9 @@
   if (!objnum)
     return;
 
+  auto* pDoc = m_pImage->GetDocument();
+  CHECK(pDoc);
+
   m_pImage.Reset();  // Clear my reference before asking the cache.
   pDoc->MaybePurgeImage(objnum);
 }
diff --git a/core/fpdfapi/page/cpdf_indexedcs.cpp b/core/fpdfapi/page/cpdf_indexedcs.cpp
index 85f694f..e35352b 100644
--- a/core/fpdfapi/page/cpdf_indexedcs.cpp
+++ b/core/fpdfapi/page/cpdf_indexedcs.cpp
@@ -20,7 +20,7 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 CPDF_IndexedCS::CPDF_IndexedCS() : CPDF_BasedCS(Family::kIndexed) {}
 
diff --git a/core/fpdfapi/page/cpdf_meshstream.cpp b/core/fpdfapi/page/cpdf_meshstream.cpp
index 95f560f..6a0419e 100644
--- a/core/fpdfapi/page/cpdf_meshstream.cpp
+++ b/core/fpdfapi/page/cpdf_meshstream.cpp
@@ -16,7 +16,7 @@
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/cfx_bitstream.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
diff --git a/core/fpdfapi/page/cpdf_pageimagecache.cpp b/core/fpdfapi/page/cpdf_pageimagecache.cpp
index 497cd8b..cebb8b1 100644
--- a/core/fpdfapi/page/cpdf_pageimagecache.cpp
+++ b/core/fpdfapi/page/cpdf_pageimagecache.cpp
@@ -6,6 +6,9 @@
 
 #include "core/fpdfapi/page/cpdf_pageimagecache.h"
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <algorithm>
 #include <utility>
 #include <vector>
@@ -16,8 +19,19 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/stl_util.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
+#include "third_party/base/check.h"
+
+#if defined(_SKIA_SUPPORT_)
+#include "core/fxcrt/data_vector.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+#include "third_party/base/notreached.h"
+#include "third_party/skia/include/core/SkImage.h"   // nogncheck
+#include "third_party/skia/include/core/SkRefCnt.h"  // nogncheck
+#endif
 
 namespace {
 
@@ -31,6 +45,86 @@
   bool operator<(const CacheInfo& other) const { return time < other.time; }
 };
 
+#if defined(_SKIA_SUPPORT_)
+// Wrapper around a `CFX_DIBBase` that memoizes `RealizeSkImage()`. This is only
+// safe if the underlying `CFX_DIBBase` is not mutable.
+class CachedImage final : public CFX_DIBBase {
+ public:
+  explicit CachedImage(RetainPtr<CFX_DIBBase> image)
+      : image_(std::move(image)) {
+    m_Format = image_->GetFormat();
+    m_Width = image_->GetWidth();
+    m_Height = image_->GetHeight();
+    m_Pitch = image_->GetPitch();
+
+    if (image_->HasPalette()) {
+      pdfium::span<const uint32_t> palette = image_->GetPaletteSpan();
+      m_palette = DataVector<uint32_t>(palette.begin(), palette.end());
+    }
+  }
+
+  pdfium::span<const uint8_t> GetBuffer() const override {
+    // TODO(crbug.com/pdfium/2051): `CachedImage` is only used by Skia, which
+    // should call `RealizeSkImage()` instead. Consider removing this, or at
+    // least making it `NOTREACHED_NORETURN()`.
+    NOTREACHED();
+    return image_->GetBuffer();
+  }
+
+  pdfium::span<const uint8_t> GetScanline(int line) const override {
+    // TODO(crbug.com/pdfium/2050): Still needed for `Realize()` call in
+    // `CPDF_ImageRenderer`.
+    return image_->GetScanline(line);
+  }
+
+  bool SkipToScanline(int line, PauseIndicatorIface* pause) const override {
+    // TODO(crbug.com/pdfium/2051): `CachedImage` is only used by Skia, which
+    // should call `RealizeSkImage()` instead. Consider removing this, or at
+    // least making it `NOTREACHED_NORETURN()`.
+    NOTREACHED();
+    return image_->SkipToScanline(line, pause);
+  }
+
+  size_t GetEstimatedImageMemoryBurden() const override {
+    // A better estimate would account for realizing the `SkImage`.
+    return image_->GetEstimatedImageMemoryBurden();
+  }
+
+  sk_sp<SkImage> RealizeSkImage() const override {
+    if (!cached_skia_image_) {
+      cached_skia_image_ = image_->RealizeSkImage();
+    }
+    return cached_skia_image_;
+  }
+
+ private:
+  RetainPtr<CFX_DIBBase> image_;
+  mutable sk_sp<SkImage> cached_skia_image_;
+};
+#endif  // defined(_SKIA_SUPPORT_)
+
+// Makes a `CachedImage` backed by `image` if Skia is the default renderer,
+// otherwise return the image itself. `realize_hint` indicates whether it would
+// be beneficial to realize `image` before caching.
+RetainPtr<CFX_DIBBase> MakeCachedImage(RetainPtr<CFX_DIBBase> image,
+                                       bool realize_hint) {
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    // TODO(crbug.com/pdfium/2050): Ignore `realize_hint`, as `RealizeSkImage()`
+    // doesn't benefit from it. The current behavior masks a bug in `CPDF_DIB`
+    // in which `GetBuffer()` and `GetScanline()` don't give the same answer.
+    if (realize_hint) {
+      image = image->Realize();
+      if (!image) {
+        return nullptr;
+      }
+    }
+    return pdfium::MakeRetain<CachedImage>(std::move(image));
+  }
+#endif  // defined(_SKIA_SUPPORT_)
+  return realize_hint ? image->Realize() : image;
+}
+
 }  // namespace
 
 CPDF_PageImageCache::CPDF_PageImageCache(CPDF_Page* pPage) : m_pPage(pPage) {}
@@ -226,13 +320,13 @@
   m_pCurMask = m_pCurBitmap.AsRaw<CPDF_DIB>()->DetachMask();
   m_dwTimeCount = pPageImageCache->GetTimeCount();
   if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) {
-    m_pCachedBitmap = m_pCurBitmap->Realize();
+    m_pCachedBitmap = MakeCachedImage(m_pCurBitmap, /*realize_hint=*/true);
     m_pCurBitmap.Reset();
   } else {
-    m_pCachedBitmap = m_pCurBitmap;
+    m_pCachedBitmap = MakeCachedImage(m_pCurBitmap, /*realize_hint=*/false);
   }
   if (m_pCurMask) {
-    m_pCachedMask = m_pCurMask->Realize();
+    m_pCachedMask = MakeCachedImage(m_pCurMask, /*realize_hint=*/true);
     m_pCurMask.Reset();
   }
   m_pCurBitmap = m_pCachedBitmap;
diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp
index 8636af6..a7b2156 100644
--- a/core/fpdfapi/page/cpdf_pageobject.cpp
+++ b/core/fpdfapi/page/cpdf_pageobject.cpp
@@ -6,6 +6,8 @@
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 
+#include <utility>
+
 #include "core/fxcrt/fx_coordinates.h"
 
 CPDF_PageObject::CPDF_PageObject(int32_t content_stream)
@@ -73,6 +75,11 @@
   return nullptr;
 }
 
+void CPDF_PageObject::SetGraphicsResourceNames(
+    std::vector<ByteString> resource_names) {
+  m_GraphicsResourceNames = std::move(resource_names);
+}
+
 void CPDF_PageObject::CopyData(const CPDF_PageObject* pSrc) {
   CopyStates(*pSrc);
   m_Rect = pSrc->m_Rect;
diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h
index 7d9d015..ccdbbaf 100644
--- a/core/fpdfapi/page/cpdf_pageobject.h
+++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -9,6 +9,8 @@
 
 #include <stdint.h>
 
+#include <vector>
+
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
 #include "core/fxcrt/bytestring.h"
@@ -94,12 +96,10 @@
     m_ResourceName = resource_name;
   }
 
-  const ByteString& GetGraphicsResourceName() const {
-    return m_GraphicsResourceName;
+  const std::vector<ByteString>& GetGraphicsResourceNames() const {
+    return m_GraphicsResourceNames;
   }
-  void SetGraphicsResourceName(const ByteString& resource_name) {
-    m_GraphicsResourceName = resource_name;
-  }
+  void SetGraphicsResourceNames(std::vector<ByteString> resource_names);
 
  protected:
   void CopyData(const CPDF_PageObject* pSrcObject);
@@ -110,8 +110,11 @@
   CPDF_ContentMarks m_ContentMarks;
   bool m_bDirty = false;
   int32_t m_ContentStream;
-  ByteString m_ResourceName;          // The resource name for this object.
-  ByteString m_GraphicsResourceName;  // Like `m_ResourceName` but for graphics.
+  // The resource name for this object.
+  ByteString m_ResourceName;
+  // Like `m_ResourceName` but for graphics. Though unlike the resource name,
+  // multiple graphics states can apply at once.
+  std::vector<ByteString> m_GraphicsResourceNames;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
diff --git a/core/fpdfapi/page/cpdf_pattern.h b/core/fpdfapi/page/cpdf_pattern.h
index cd535b1..b773817 100644
--- a/core/fpdfapi/page/cpdf_pattern.h
+++ b/core/fpdfapi/page/cpdf_pattern.h
@@ -22,8 +22,6 @@
   // Values used in PDFs. Do not change.
   enum PatternType { kTiling = 1, kShading = 2 };
 
-  ~CPDF_Pattern() override;
-
   virtual CPDF_TilingPattern* AsTilingPattern();
   virtual CPDF_ShadingPattern* AsShadingPattern();
 
@@ -33,6 +31,7 @@
   CPDF_Pattern(CPDF_Document* pDoc,
                RetainPtr<CPDF_Object> pObj,
                const CFX_Matrix& parentMatrix);
+  ~CPDF_Pattern() override;
 
   // All the getters that return pointers return non-NULL pointers.
   CPDF_Document* document() const { return m_pDocument; }
diff --git a/core/fpdfapi/page/cpdf_psengine.h b/core/fpdfapi/page/cpdf_psengine.h
index d7f6f7e..0b82bc0 100644
--- a/core/fpdfapi/page/cpdf_psengine.h
+++ b/core/fpdfapi/page/cpdf_psengine.h
@@ -14,7 +14,7 @@
 #include <vector>
 
 #include "core/fxcrt/bytestring.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_PSEngine;
 class CPDF_PSProc;
diff --git a/core/fpdfapi/page/cpdf_sampledfunc.cpp b/core/fpdfapi/page/cpdf_sampledfunc.cpp
index 0aa28f4..1dd9a5b 100644
--- a/core/fpdfapi/page/cpdf_sampledfunc.cpp
+++ b/core/fpdfapi/page/cpdf_sampledfunc.cpp
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_sampledfunc.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -15,7 +16,6 @@
 #include "core/fxcrt/cfx_bitstream.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/small_buffer.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -116,8 +116,8 @@
     encoded_input[i] =
         Interpolate(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1],
                     m_EncodeInfo[i].encode_min, m_EncodeInfo[i].encode_max);
-    index[i] = pdfium::clamp(static_cast<uint32_t>(encoded_input[i]), 0U,
-                             m_EncodeInfo[i].sizes - 1);
+    index[i] = std::clamp(static_cast<uint32_t>(encoded_input[i]), 0U,
+                          m_EncodeInfo[i].sizes - 1);
     pos += index[i] * blocksize[i];
   }
   FX_SAFE_INT32 bits_to_output = m_nOutputs;
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index 0522cf1..cae9d72 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -43,19 +43,22 @@
 #include "core/fxge/cfx_graphstatedata.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/no_destructor.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
 
 namespace {
 
-const int kMaxFormLevel = 40;
+constexpr int kMaxFormLevel = 40;
 
-const int kSingleCoordinatePair = 1;
-const int kTensorCoordinatePairs = 16;
-const int kCoonsCoordinatePairs = 12;
-const int kSingleColorPerPatch = 1;
-const int kQuadColorsPerPatch = 4;
+// Upper limit for the number of form XObjects within a form XObject.
+constexpr int kFormCountLimit = 4096;
+
+constexpr int kSingleCoordinatePair = 1;
+constexpr int kTensorCoordinatePairs = 16;
+constexpr int kCoonsCoordinatePairs = 12;
+constexpr int kSingleColorPerPatch = 1;
+constexpr int kQuadColorsPerPatch = 4;
 
 const char kPathOperatorSubpath = 'm';
 const char kPathOperatorLine = 'l';
@@ -250,7 +253,7 @@
     RetainPtr<CPDF_Dictionary> pResources,
     const CFX_FloatRect& rcBBox,
     const CPDF_AllStates* pStates,
-    std::set<const uint8_t*>* pParsedSet)
+    CPDF_Form::RecursionState* recursion_state)
     : m_pDocument(pDocument),
       m_pPageResources(pPageResources),
       m_pParentResources(pParentResources),
@@ -258,7 +261,7 @@
                                                   pParentResources.Get(),
                                                   pPageResources.Get())),
       m_pObjectHolder(pObjHolder),
-      m_ParsedSet(pParsedSet),
+      m_RecursionState(recursion_state),
       m_BBox(rcBBox),
       m_pCurStates(std::make_unique<CPDF_AllStates>()) {
   if (pmtContentToUser)
@@ -426,7 +429,7 @@
   if (bText) {
     pObj->m_TextState = m_pCurStates->m_TextState;
   }
-  pObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName);
+  pObj->SetGraphicsResourceNames(m_pCurStates->m_GraphicsResourceNames);
 }
 
 // static
@@ -744,7 +747,16 @@
     type = pXObject->GetDict()->GetByteStringFor("Subtype");
 
   if (type == "Form") {
+    if (m_RecursionState->form_count > kFormCountLimit) {
+      return;
+    }
+
+    const bool is_first = m_RecursionState->form_count == 0;
+    ++m_RecursionState->form_count;
     AddForm(std::move(pXObject), name);
+    if (is_first) {
+      m_RecursionState->form_count = 0;
+    }
     return;
   }
 
@@ -772,13 +784,13 @@
   status.m_TextState = m_pCurStates->m_TextState;
   auto form = std::make_unique<CPDF_Form>(
       m_pDocument, m_pPageResources, std::move(pStream), m_pResources.Get());
-  form->ParseContent(&status, nullptr, m_ParsedSet);
+  form->ParseContent(&status, nullptr, m_RecursionState);
 
   CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser;
   auto pFormObj = std::make_unique<CPDF_FormObject>(GetCurrentStreamIndex(),
                                                     std::move(form), matrix);
   pFormObj->SetResourceName(name);
-  pFormObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName);
+  pFormObj->SetGraphicsResourceNames(m_pCurStates->m_GraphicsResourceNames);
   if (!m_pObjectHolder->BackgroundAlphaNeeded() &&
       pFormObj->form()->BackgroundAlphaNeeded()) {
     m_pObjectHolder->SetBackgroundAlphaNeeded(true);
@@ -903,7 +915,8 @@
   if (!pGS)
     return;
 
-  m_pCurStates->m_GraphicsResourceName = name;
+  CHECK(!name.IsEmpty());
+  m_pCurStates->m_GraphicsResourceNames.push_back(std::move(name));
   m_pCurStates->ProcessExtGS(pGS.Get(), this);
 }
 
@@ -1535,15 +1548,15 @@
   // Parsing will be done from within |pDataStart|.
   pdfium::span<const uint8_t> pDataStart = pData.subspan(start_offset);
   m_StartParseOffset = start_offset;
-  if (m_ParsedSet->size() > kMaxFormLevel ||
-      pdfium::Contains(*m_ParsedSet, pDataStart.data())) {
+  if (m_RecursionState->parsed_set.size() > kMaxFormLevel ||
+      pdfium::Contains(m_RecursionState->parsed_set, pDataStart.data())) {
     return fxcrt::CollectionSize<uint32_t>(pDataStart);
   }
 
   m_StreamStartOffsets = stream_start_offsets;
 
-  ScopedSetInsertion<const uint8_t*> scopedInsert(m_ParsedSet,
-                                                  pDataStart.data());
+  ScopedSetInsertion<const uint8_t*> scoped_insert(
+      &m_RecursionState->parsed_set, pDataStart.data());
 
   uint32_t init_obj_count = m_pObjectHolder->GetPageObjectCount();
   AutoNuller<std::unique_ptr<CPDF_StreamParser>> auto_clearer(&m_pSyntax);
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h
index 276dc72..c31dd15 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.h
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.h
@@ -9,11 +9,11 @@
 
 #include <map>
 #include <memory>
-#include <set>
 #include <stack>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
+#include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_number.h"
@@ -21,7 +21,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_path.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_AllStates;
 class CPDF_ColorSpace;
@@ -49,7 +49,7 @@
                            RetainPtr<CPDF_Dictionary> pResources,
                            const CFX_FloatRect& rcBBox,
                            const CPDF_AllStates* pStates,
-                           std::set<const uint8_t*>* pParsedSet);
+                           CPDF_Form::RecursionState* parse_state);
   ~CPDF_StreamContentParser();
 
   uint32_t Parse(pdfium::span<const uint8_t> pData,
@@ -222,7 +222,7 @@
   RetainPtr<CPDF_Dictionary> const m_pParentResources;
   RetainPtr<CPDF_Dictionary> const m_pResources;
   UnownedPtr<CPDF_PageObjectHolder> const m_pObjectHolder;
-  UnownedPtr<std::set<const uint8_t*>> const m_ParsedSet;
+  UnownedPtr<CPDF_Form::RecursionState> const m_RecursionState;
   CFX_Matrix m_mtContentToUser;
   const CFX_FloatRect m_BBox;
   uint32_t m_ParamStartPos = 0;
diff --git a/core/fpdfapi/page/cpdf_streamparser.h b/core/fpdfapi/page/cpdf_streamparser.h
index 1a0f115..ecefa02 100644
--- a/core/fpdfapi/page/cpdf_streamparser.h
+++ b/core/fpdfapi/page/cpdf_streamparser.h
@@ -10,7 +10,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Document;
diff --git a/core/fpdfapi/page/cpdf_textobject.cpp b/core/fpdfapi/page/cpdf_textobject.cpp
index a063541..087883e 100644
--- a/core/fpdfapi/page/cpdf_textobject.cpp
+++ b/core/fpdfapi/page/cpdf_textobject.cpp
@@ -12,7 +12,7 @@
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #define ISLATINWORD(u) (u != 0x20 && u <= 0x28FF)
 
diff --git a/core/fpdfapi/page/cpdf_textstate.h b/core/fpdfapi/page/cpdf_textstate.h
index 932d5a7..b462606 100644
--- a/core/fpdfapi/page/cpdf_textstate.h
+++ b/core/fpdfapi/page/cpdf_textstate.h
@@ -10,7 +10,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Document;
 class CPDF_Font;
diff --git a/core/fpdfapi/page/cpdf_transferfunc.h b/core/fpdfapi/page/cpdf_transferfunc.h
index d168930..11ea98d 100644
--- a/core/fpdfapi/page/cpdf_transferfunc.h
+++ b/core/fpdfapi/page/cpdf_transferfunc.h
@@ -13,7 +13,7 @@
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 
diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.h b/core/fpdfapi/page/cpdf_transferfuncdib.h
index a034116..8173109 100644
--- a/core/fpdfapi/page/cpdf_transferfuncdib.h
+++ b/core/fpdfapi/page/cpdf_transferfuncdib.h
@@ -12,7 +12,7 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/cfx_dibbase.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_TransferFunc;
 
diff --git a/core/fpdfapi/parser/cfdf_document.cpp b/core/fpdfapi/parser/cfdf_document.cpp
index 5e9b6d3..ea34fbe 100644
--- a/core/fpdfapi/parser/cfdf_document.cpp
+++ b/core/fpdfapi/parser/cfdf_document.cpp
@@ -15,7 +15,7 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_string_wrappers.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 CFDF_Document::CFDF_Document() = default;
 
diff --git a/core/fpdfapi/parser/cfdf_document.h b/core/fpdfapi/parser/cfdf_document.h
index bd0e72f..f327ab5 100644
--- a/core/fpdfapi/parser/cfdf_document.h
+++ b/core/fpdfapi/parser/cfdf_document.h
@@ -11,7 +11,7 @@
 
 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class IFX_SeekableReadStream;
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
index 7f0b7fe..8562e64 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
@@ -11,7 +11,6 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
@@ -54,11 +53,6 @@
         break;
       case State::kDone:
         break;
-      default: {
-        status_ = CPDF_DataAvail::kDataError;
-        NOTREACHED();
-        break;
-      }
     }
     if (!check_result)
       break;
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
index 0bb5bb6..31177a3 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp
@@ -110,20 +110,30 @@
   UpdateTrailer(std::move(new_cross_ref->trailer_));
 }
 
-void CPDF_CrossRefTable::ShrinkObjectMap(uint32_t objnum) {
-  if (objnum == 0) {
+void CPDF_CrossRefTable::SetObjectMapSize(uint32_t size) {
+  if (size == 0) {
     objects_info_.clear();
     return;
   }
 
-  objects_info_.erase(objects_info_.lower_bound(objnum), objects_info_.end());
+  objects_info_.erase(objects_info_.lower_bound(size), objects_info_.end());
 
-  if (!pdfium::Contains(objects_info_, objnum - 1))
-    objects_info_[objnum - 1].pos = 0;
+  if (!pdfium::Contains(objects_info_, size - 1)) {
+    objects_info_[size - 1].pos = 0;
+  }
 }
 
 void CPDF_CrossRefTable::UpdateInfo(
-    std::map<uint32_t, ObjectInfo>&& new_objects_info) {
+    std::map<uint32_t, ObjectInfo> new_objects_info) {
+  if (new_objects_info.empty()) {
+    return;
+  }
+
+  if (objects_info_.empty()) {
+    objects_info_ = std::move(new_objects_info);
+    return;
+  }
+
   auto cur_it = objects_info_.begin();
   auto new_it = new_objects_info.begin();
   while (cur_it != objects_info_.end() && new_it != new_objects_info.end()) {
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.h b/core/fpdfapi/parser/cpdf_cross_ref_table.h
index 246e129..d76e29c 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_table.h
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.h
@@ -73,10 +73,11 @@
 
   void Update(std::unique_ptr<CPDF_CrossRefTable> new_cross_ref);
 
-  void ShrinkObjectMap(uint32_t objnum);
+  // Objects with object number >= `size` will be removed.
+  void SetObjectMapSize(uint32_t size);
 
  private:
-  void UpdateInfo(std::map<uint32_t, ObjectInfo>&& new_objects_info);
+  void UpdateInfo(std::map<uint32_t, ObjectInfo> new_objects_info);
   void UpdateTrailer(RetainPtr<CPDF_Dictionary> new_trailer);
 
   RetainPtr<CPDF_Dictionary> trailer_;
diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.cpp b/core/fpdfapi/parser/cpdf_crypto_handler.cpp
index 5404207..43ae781 100644
--- a/core/fpdfapi/parser/cpdf_crypto_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_crypto_handler.cpp
@@ -232,7 +232,7 @@
     return false;
 
   struct MayBeSignature {
-    const CPDF_Dictionary* parent;
+    RetainPtr<const CPDF_Dictionary> parent;
     RetainPtr<CPDF_Object> contents;
   };
 
@@ -255,7 +255,8 @@
         // Temporary skip it, to prevent signature corruption.
         // It will be decrypted on next interations, if this is not contents of
         // signature dictionary.
-        may_be_sign_dictionaries.push({parent_dict.Get(), std::move(child)});
+        may_be_sign_dictionaries.push(
+            {std::move(parent_dict), std::move(child)});
         walker.SkipWalkIntoCurrentObject();
         continue;
       }
diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.h b/core/fpdfapi/parser/cpdf_crypto_handler.h
index b8f938c..ba934cc 100644
--- a/core/fpdfapi/parser/cpdf_crypto_handler.h
+++ b/core/fpdfapi/parser/cpdf_crypto_handler.h
@@ -17,7 +17,7 @@
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
diff --git a/core/fpdfapi/parser/cpdf_data_avail.cpp b/core/fpdfapi/parser/cpdf_data_avail.cpp
index 7db348a..bc3ff33 100644
--- a/core/fpdfapi/parser/cpdf_data_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_data_avail.cpp
@@ -193,9 +193,6 @@
     case kDataError:
       m_internalStatus = InternalStatus::kError;
       return false;
-    default:
-      NOTREACHED();
-      return false;
   }
 
   if (!m_parser.LoadAllCrossRefV4(m_pCrossRefAvail->last_crossref_offset()) &&
@@ -421,9 +418,6 @@
     case kDataError:
       m_internalStatus = InternalStatus::kError;
       return true;
-    default:
-      NOTREACHED();
-      return false;
   }
 }
 
@@ -488,9 +482,6 @@
       return kLinearizationUnknown;
     case kDataError:
       return kNotLinearized;
-    default:
-      NOTREACHED();
-      return kLinearizationUnknown;
   }
 }
 
@@ -1002,10 +993,7 @@
       return kFormNotAvailable;
     case kDataAvailable:
       return kFormAvailable;
-    default:
-      NOTREACHED();
   }
-  return kFormError;
 }
 
 bool CPDF_DataAvail::ValidatePage(uint32_t dwPage) const {
diff --git a/core/fpdfapi/parser/cpdf_encryptor.h b/core/fpdfapi/parser/cpdf_encryptor.h
index 37f0763..d0aabcc 100644
--- a/core/fpdfapi/parser/cpdf_encryptor.h
+++ b/core/fpdfapi/parser/cpdf_encryptor.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_CryptoHandler;
 
diff --git a/core/fpdfapi/parser/cpdf_flateencoder.h b/core/fpdfapi/parser/cpdf_flateencoder.h
index 2d26d9b..a9b37f7 100644
--- a/core/fpdfapi/parser/cpdf_flateencoder.h
+++ b/core/fpdfapi/parser/cpdf_flateencoder.h
@@ -12,7 +12,7 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Encryptor;
diff --git a/core/fpdfapi/parser/cpdf_hint_tables.cpp b/core/fpdfapi/parser/cpdf_hint_tables.cpp
index ba3173d..5870d01 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables.cpp
+++ b/core/fpdfapi/parser/cpdf_hint_tables.cpp
@@ -21,8 +21,8 @@
 #include "core/fxcrt/cfx_bitstream.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 namespace {
 
diff --git a/core/fpdfapi/parser/cpdf_linearized_header.cpp b/core/fpdfapi/parser/cpdf_linearized_header.cpp
index f4de7d7..b093578 100644
--- a/core/fpdfapi/parser/cpdf_linearized_header.cpp
+++ b/core/fpdfapi/parser/cpdf_linearized_header.cpp
@@ -17,7 +17,7 @@
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/check.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
diff --git a/core/fpdfapi/parser/cpdf_object_stream.cpp b/core/fpdfapi/parser/cpdf_object_stream.cpp
index 1c5a7b7..19294da 100644
--- a/core/fpdfapi/parser/cpdf_object_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_object_stream.cpp
@@ -17,7 +17,7 @@
 #include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/check.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index 302aec1..098e67e 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -35,14 +35,15 @@
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
 
 namespace {
 
 // A limit on the size of the xref table. Theoretical limits are higher, but
-// this may be large enough in practice.
-const int32_t kMaxXRefSize = 1048576;
+// this may be large enough in practice. The max size should always be 1 more
+// than the max object number.
+constexpr int32_t kMaxXRefSize = CPDF_Parser::kMaxObjectNumber + 1;
 
 // "%PDF-1.7\n"
 constexpr FX_FILESIZE kPDFHeaderSize = 9;
@@ -200,10 +201,6 @@
   return GetObjectType(objnum) == ObjectType::kFree;
 }
 
-void CPDF_Parser::ShrinkObjectMap(uint32_t size) {
-  m_CrossRefTable->ShrinkObjectMap(size);
-}
-
 bool CPDF_Parser::InitSyntaxParser(RetainPtr<CPDF_ReadValidator> validator) {
   const absl::optional<FX_FILESIZE> header_offset = GetHeaderOffset(validator);
   if (!header_offset.has_value())
@@ -384,7 +381,7 @@
   m_CrossRefTable->SetTrailer(std::move(trailer), kNoV4TrailerObjectNumber);
   const int32_t xrefsize = GetTrailer()->GetDirectIntegerFor("Size");
   if (xrefsize > 0 && xrefsize <= kMaxXRefSize)
-    ShrinkObjectMap(xrefsize);
+    m_CrossRefTable->SetObjectMapSize(xrefsize);
 
   FX_FILESIZE xref_stm = GetTrailer()->GetDirectIntegerFor("XRefStm");
   std::vector<FX_FILESIZE> xref_stream_list{xref_stm};
@@ -648,8 +645,6 @@
         m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive.obj_num,
                                        obj.info.archive.obj_index);
         break;
-      default:
-        NOTREACHED();
     }
   }
 }
@@ -778,7 +773,7 @@
   if (bMainXRef) {
     m_CrossRefTable = std::make_unique<CPDF_CrossRefTable>(
         std::move(pNewTrailer), pStream->GetObjNum());
-    m_CrossRefTable->ShrinkObjectMap(size);
+    m_CrossRefTable->SetObjectMapSize(size);
   } else {
     m_CrossRefTable = CPDF_CrossRefTable::MergeUp(
         std::make_unique<CPDF_CrossRefTable>(std::move(pNewTrailer),
@@ -815,17 +810,33 @@
 
     pdfium::span<const uint8_t> seg_span = data_span.subspan(
         segindex * total_width, index.obj_count * total_width);
-    FX_SAFE_UINT32 dwMaxObjNum = index.start_obj_num;
-    dwMaxObjNum += index.obj_count;
-    uint32_t dwV5Size =
-        m_CrossRefTable->objects_info().empty() ? 0 : GetLastObjNum() + 1;
-    if (!dwMaxObjNum.IsValid() || dwMaxObjNum.ValueOrDie() > dwV5Size)
+    FX_SAFE_UINT32 safe_new_size = index.start_obj_num;
+    safe_new_size += index.obj_count;
+    if (!safe_new_size.IsValid()) {
       continue;
+    }
+
+    // Until SetObjectMapSize() below has been called by a prior loop iteration,
+    // `current_size` is based on the /Size value parsed in LoadCrossRefV5().
+    // PDFs may not always have the correct /Size. In this case, other PDF
+    // implementations ignore the incorrect size, and PDFium also ignores
+    // incorrect size in trailers for V4 xrefs.
+    const uint32_t current_size =
+        m_CrossRefTable->objects_info().empty() ? 0 : GetLastObjNum() + 1;
+    // So allow `new_size` to be greater than `current_size`, but avoid going
+    // over `kMaxXRefSize`. This works just fine because the loop below checks
+    // against `kMaxObjectNumber`, and the two "max" constants are in sync.
+    const uint32_t new_size =
+        std::min<uint32_t>(safe_new_size.ValueOrDie(), kMaxXRefSize);
+    if (new_size > current_size) {
+      m_CrossRefTable->SetObjectMapSize(new_size);
+    }
 
     for (uint32_t i = 0; i < index.obj_count; ++i) {
       const uint32_t obj_num = index.start_obj_num + i;
-      if (obj_num >= CPDF_Parser::kMaxObjectNumber)
+      if (obj_num >= kMaxObjectNumber) {
         break;
+      }
 
       ProcessCrossRefV5Entry(seg_span.subspan(i * total_width, total_width),
                              field_widths, obj_num);
@@ -841,13 +852,18 @@
     pdfium::span<const uint32_t> field_widths,
     uint32_t obj_num) {
   DCHECK_GE(field_widths.size(), kMinFieldCount);
-  ObjectType type = ObjectType::kNotCompressed;
+  ObjectType type;
   if (field_widths[0]) {
     const uint32_t cross_ref_stream_obj_type =
         GetFirstXRefStreamEntry(entry_span, field_widths);
     type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type);
     if (type == ObjectType::kNull)
       return;
+  } else {
+    // Per ISO 32000-1:2008 table 17, use the default value of 1 for the xref
+    // stream entry when it is not specified. The `type` assignment is the
+    // equivalent to calling GetObjectTypeFromCrossRefStreamType(1).
+    type = ObjectType::kNotCompressed;
   }
 
   const ObjectType existing_type = GetObjectType(obj_num);
diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h
index 21dd8aa..09dc724 100644
--- a/core/fpdfapi/parser/cpdf_parser.h
+++ b/core/fpdfapi/parser/cpdf_parser.h
@@ -159,7 +159,6 @@
   bool LoadLinearizedAllCrossRefV5(FX_FILESIZE main_xref_offset);
   Error LoadLinearizedMainXRefTable();
   const CPDF_ObjectStream* GetObjectStream(uint32_t object_number);
-  void ShrinkObjectMap(uint32_t size);
   // A simple check whether the cross reference table matches with
   // the objects.
   bool VerifyCrossRefV4();
diff --git a/core/fpdfapi/parser/cpdf_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
index efde90e..0759807 100644
--- a/core/fpdfapi/parser/cpdf_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
@@ -6,6 +6,7 @@
 
 #include <limits>
 #include <memory>
+#include <ostream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -22,14 +23,16 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
 
+using testing::ElementsAre;
+using testing::Pair;
 using testing::Return;
 
 namespace {
 
-CPDF_CrossRefTable::ObjectInfo GetObjInfo(const CPDF_Parser& parser,
-                                          uint32_t obj_num) {
+CPDF_Parser::ObjectInfo GetObjInfo(const CPDF_Parser& parser,
+                                   uint32_t obj_num) {
   const auto* info = parser.GetCrossRefTable()->GetObjectInfo(obj_num);
-  return info ? *info : CPDF_CrossRefTable::ObjectInfo();
+  return info ? *info : CPDF_Parser::ObjectInfo();
 }
 
 class TestObjectsHolder final : public CPDF_Parser::ParsedObjectsHolder {
@@ -44,6 +47,54 @@
 
 }  // namespace
 
+// Test-only helper to support Gmock. Cannot be in an anonymous namespace.
+bool operator==(const CPDF_Parser::ObjectInfo& lhs,
+                const CPDF_Parser::ObjectInfo& rhs) {
+  if (lhs.type != rhs.type) {
+    return false;
+  }
+
+  if (lhs.gennum != rhs.gennum) {
+    return false;
+  }
+
+  switch (lhs.type) {
+    case CPDF_Parser::ObjectType::kFree:
+      return true;
+    case CPDF_Parser::ObjectType::kNormal:
+      return lhs.pos == rhs.pos;
+    case CPDF_Parser::ObjectType::kCompressed:
+      return lhs.archive.obj_num == rhs.archive.obj_num &&
+             lhs.archive.obj_index == rhs.archive.obj_index;
+    case CPDF_Parser::ObjectType::kObjStream:
+      return false;
+  }
+}
+
+// Test-only helper to let Gmock pretty-print `info`. Cannot be in an anonymous
+// namespace.
+std::ostream& operator<<(std::ostream& os,
+                         const CPDF_Parser::ObjectInfo& info) {
+  os << "(";
+  switch (info.type) {
+    case CPDF_Parser::ObjectType::kFree:
+      os << "Free object";
+      break;
+    case CPDF_Parser::ObjectType::kNormal:
+      os << "Normal object, pos: " << info.pos;
+      break;
+    case CPDF_Parser::ObjectType::kCompressed:
+      os << "Compressed object, archive obj_num: " << info.archive.obj_num
+         << ", archive obj_index: " << info.archive.obj_index;
+      break;
+    case CPDF_Parser::ObjectType::kObjStream:
+      os << "ObjectStream object";
+      break;
+  }
+  os << ", gennum: " << info.gennum << ")";
+  return os;
+}
+
 // A wrapper class to help test member functions of CPDF_Parser.
 class CPDF_TestParser final : public CPDF_Parser {
  public:
@@ -361,15 +412,28 @@
   EXPECT_EQ(0u, parser.GetCrossRefTable()->objects_info().size());
 }
 
-TEST(ParserTest, XrefObjectIndicesTooBig) {
-  CPDF_TestParser parser;
+class ParserXRefTest : public testing::Test {
+ public:
+  ParserXRefTest() = default;
+  ~ParserXRefTest() override = default;
 
-  // Satisfy CPDF_Parser's checks, so the test data below can concentrate on the
-  // /XRef stream and avoid also providing other valid dictionaries.
-  auto dummy_root = pdfium::MakeRetain<CPDF_Dictionary>();
-  EXPECT_CALL(parser.object_holder(), ParseIndirectObject)
-      .WillRepeatedly(Return(dummy_root));
+  // testing::Test:
+  void SetUp() override {
+    // Satisfy CPDF_Parser's checks, so the test data below can concentrate on
+    // the /XRef stream and avoid also providing other valid dictionaries.
+    dummy_root_ = pdfium::MakeRetain<CPDF_Dictionary>();
+    EXPECT_CALL(parser().object_holder(), ParseIndirectObject)
+        .WillRepeatedly(Return(dummy_root_));
+  }
 
+  CPDF_TestParser& parser() { return parser_; }
+
+ private:
+  RetainPtr<CPDF_Dictionary> dummy_root_;
+  CPDF_TestParser parser_;
+};
+
+TEST_F(ParserXRefTest, XrefObjectIndicesTooBig) {
   // Since /Index starts at 4194303, the object number will go past
   // `kMaxObjectNumber`.
   static_assert(CPDF_Parser::kMaxObjectNumber == 4194304,
@@ -392,36 +456,30 @@
       "startxref\n"
       "14\n"
       "%%EOF\n";
-  ASSERT_TRUE(parser.InitTestFromBuffer(kData));
-  EXPECT_EQ(CPDF_Parser::SUCCESS, parser.StartParseInternal());
-  ASSERT_TRUE(parser.GetCrossRefTable());
-  const auto& objects_info = parser.GetCrossRefTable()->objects_info();
-  EXPECT_EQ(2u, objects_info.size());
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+  ASSERT_TRUE(parser().GetCrossRefTable());
+  const auto& objects_info = parser().GetCrossRefTable()->objects_info();
 
   // This should be the only object from table. Subsequent objects have object
   // numbers that are too big.
-  auto first_object_it = objects_info.find(4194303);
-  ASSERT_NE(first_object_it, objects_info.end());
-  EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, first_object_it->second.type);
-  EXPECT_EQ(0, first_object_it->second.pos);
+  CPDF_Parser::ObjectInfo only_valid_object;
+  only_valid_object.type = CPDF_Parser::ObjectType::kNormal;
+  only_valid_object.pos = 0;
 
   // TODO(thestig): Should the xref table contain object 4194305?
   // Consider reworking CPDF_Parser's object representation to avoid having to
   // store this placeholder object.
-  auto placeholder_object_it = objects_info.find(4194305);
-  ASSERT_NE(placeholder_object_it, objects_info.end());
-  EXPECT_EQ(CPDF_Parser::ObjectType::kFree, placeholder_object_it->second.type);
+  CPDF_Parser::ObjectInfo placeholder_object;
+  placeholder_object.type = CPDF_Parser::ObjectType::kFree;
+  placeholder_object.pos = 0;
+
+  EXPECT_THAT(objects_info, ElementsAre(Pair(4194303, only_valid_object),
+                                        Pair(4194305, placeholder_object)));
 }
 
-TEST(ParserTest, XrefHasInvalidArchiveObjectNumber) {
-  CPDF_TestParser parser;
-
-  // Satisfy CPDF_Parser's checks, so the test data below can concentrate on the
-  // /XRef stream and avoid also providing other valid dictionaries.
-  auto dummy_root = pdfium::MakeRetain<CPDF_Dictionary>();
-  EXPECT_CALL(parser.object_holder(), ParseIndirectObject)
-      .WillRepeatedly(Return(dummy_root));
-
+TEST_F(ParserXRefTest, XrefHasInvalidArchiveObjectNumber) {
   // 0xFF in the first object in the xref object stream is invalid.
   const unsigned char kData[] =
       "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
@@ -440,23 +498,325 @@
       "startxref\n"
       "14\n"
       "%%EOF\n";
-  ASSERT_TRUE(parser.InitTestFromBuffer(kData));
-  EXPECT_EQ(CPDF_Parser::SUCCESS, parser.StartParseInternal());
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
 
-  const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable();
+  const CPDF_CrossRefTable* cross_ref_table = parser().GetCrossRefTable();
   ASSERT_TRUE(cross_ref_table);
   EXPECT_EQ(7u, cross_ref_table->trailer_object_number());
   const auto& objects_info = cross_ref_table->objects_info();
-  EXPECT_EQ(2u, objects_info.size());
 
-  // Skip over the first object, and continue parsing the remaining objects.
-  auto second_object_it = objects_info.find(1);
-  ASSERT_NE(second_object_it, objects_info.end());
-  EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, second_object_it->second.type);
-  EXPECT_EQ(15, second_object_it->second.pos);
+  // The expectation is for the parser to skip over the first object, and
+  // continue parsing the remaining objects. So these are the second and third
+  // objects.
+  CPDF_Parser::ObjectInfo expected_objects[2];
+  expected_objects[0].type = CPDF_Parser::ObjectType::kNormal;
+  expected_objects[0].pos = 15;
+  expected_objects[1].type = CPDF_Parser::ObjectType::kNormal;
+  expected_objects[1].pos = 18;
 
-  auto third_object_it = objects_info.find(2);
-  ASSERT_NE(third_object_it, objects_info.end());
-  EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, third_object_it->second.type);
-  EXPECT_EQ(18, third_object_it->second.pos);
+  EXPECT_THAT(objects_info, ElementsAre(Pair(1, expected_objects[0]),
+                                        Pair(2, expected_objects[1])));
+}
+
+TEST_F(ParserXRefTest, XrefHasInvalidObjectType) {
+  // The XRef object is a dictionary and not a stream.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 3\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser().StartParseInternal());
+}
+
+TEST_F(ParserXRefTest, XrefHasInvalidPrevValue) {
+  // The /Prev value is an absolute offset, so it should never be negative.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 3\n"
+      "  /W [1 1 1]\n"
+      "  /Prev -1\n"
+      ">>\n"
+      "stream\n"
+      "02 FF 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser().StartParseInternal());
+}
+
+TEST_F(ParserXRefTest, XrefHasInvalidSizeValue) {
+  // The /Size value should never be negative.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 3\n"
+      "  /W [1 1 1]\n"
+      "  /Size -1\n"
+      ">>\n"
+      "stream\n"
+      "02 FF 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::FORMAT_ERROR, parser().StartParseInternal());
+}
+
+TEST_F(ParserXRefTest, XrefHasInvalidWidth) {
+  // The /W array needs to have at least 3 values.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 3\n"
+      "  /W [1 1]\n"
+      ">>\n"
+      "stream\n"
+      "02 FF 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+
+  // StartParseInternal() succeeded not because XRef parsing succeeded, but
+  // because RebuildCrossRef() got lucky with the data stream. Therefore, don't
+  // bother checking the garbage output.
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_TRUE(parser().xref_table_rebuilt());
+}
+
+TEST_F(ParserXRefTest, XrefFirstWidthEntryIsZero) {
+  // When the first /W array entry is 0, it implies the objects are all of the
+  // normal type.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 2\n"
+      "  /W [0 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "0F 00\n"
+      "12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+  ASSERT_TRUE(parser().GetCrossRefTable());
+  const auto& objects_info = parser().GetCrossRefTable()->objects_info();
+
+  CPDF_Parser::ObjectInfo expected_result[2];
+  expected_result[0].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[0].pos = 15;
+  expected_result[1].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[1].pos = 18;
+  EXPECT_THAT(objects_info, ElementsAre(Pair(0, expected_result[0]),
+                                        Pair(1, expected_result[1])));
+}
+
+TEST_F(ParserXRefTest, XrefWithValidIndex) {
+  // The /Index specifies objects (2), (4, 5), (80, 81, 82).
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 83\n"
+      "  /Index [2 1 4 2 80 3]\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "01 00 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "01 20 00\n"
+      "01 22 00\n"
+      "01 25 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+  ASSERT_TRUE(parser().GetCrossRefTable());
+  const auto& objects_info = parser().GetCrossRefTable()->objects_info();
+
+  CPDF_Parser::ObjectInfo expected_result[6];
+  expected_result[0].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[0].pos = 0;
+  expected_result[1].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[1].pos = 15;
+  expected_result[2].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[2].pos = 18;
+  expected_result[3].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[3].pos = 32;
+  expected_result[4].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[4].pos = 34;
+  expected_result[5].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[5].pos = 37;
+  EXPECT_THAT(
+      objects_info,
+      ElementsAre(Pair(2, expected_result[0]), Pair(4, expected_result[1]),
+                  Pair(5, expected_result[2]), Pair(80, expected_result[3]),
+                  Pair(81, expected_result[4]), Pair(82, expected_result[5])));
+}
+
+TEST_F(ParserXRefTest, XrefIndexWithRepeatedObject) {
+  // The /Index specifies objects (2, 3), (3). AKA the sub-sections overlap.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 4\n"
+      "  /Index [2 2 3 1]\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "01 00 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+  ASSERT_TRUE(parser().GetCrossRefTable());
+  const auto& objects_info = parser().GetCrossRefTable()->objects_info();
+
+  CPDF_Parser::ObjectInfo expected_result[2];
+  expected_result[0].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[0].pos = 0;
+  expected_result[1].type = CPDF_Parser::ObjectType::kNormal;
+  // Since the /Index does not follow the spec, this is one of the 2 possible
+  // values that a parser can come up with.
+  expected_result[1].pos = 15;
+  EXPECT_THAT(objects_info, ElementsAre(Pair(2, expected_result[0]),
+                                        Pair(3, expected_result[1])));
+}
+
+TEST_F(ParserXRefTest, XrefIndexWithOutOfOrderObjects) {
+  // The /Index specifies objects (3, 4), (2), which is not in ascending order.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 5\n"
+      "  /Index [3 2 2 1]\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "01 00 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+  ASSERT_TRUE(parser().GetCrossRefTable());
+  const auto& objects_info = parser().GetCrossRefTable()->objects_info();
+
+  // Although the /Index does not follow the spec, the parser tolerates it.
+  CPDF_Parser::ObjectInfo expected_result[3];
+  expected_result[0].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[0].pos = 18;
+  expected_result[1].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[1].pos = 0;
+  expected_result[2].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[2].pos = 15;
+  EXPECT_THAT(objects_info, ElementsAre(Pair(2, expected_result[0]),
+                                        Pair(3, expected_result[1]),
+                                        Pair(4, expected_result[2])));
+}
+
+TEST_F(ParserXRefTest, XrefWithIndexAndWrongSize) {
+  // The /Index specifies objects (2), (80, 81), so the /Size should be 82,
+  // but is actually 81.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 81\n"
+      "  /Index [2 1 80 2]\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "01 00 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+
+  ASSERT_TRUE(parser().InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser().StartParseInternal());
+  EXPECT_FALSE(parser().xref_table_rebuilt());
+  ASSERT_TRUE(parser().GetCrossRefTable());
+  const auto& objects_info = parser().GetCrossRefTable()->objects_info();
+
+  CPDF_Parser::ObjectInfo expected_result[3];
+  expected_result[0].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[0].pos = 0;
+  expected_result[1].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[1].pos = 15;
+  expected_result[2].type = CPDF_Parser::ObjectType::kNormal;
+  expected_result[2].pos = 18;
+  EXPECT_THAT(objects_info, ElementsAre(Pair(2, expected_result[0]),
+                                        Pair(80, expected_result[1]),
+                                        Pair(81, expected_result[2])));
 }
diff --git a/core/fpdfapi/parser/cpdf_security_handler.cpp b/core/fpdfapi/parser/cpdf_security_handler.cpp
index ff17c59..5f2883d 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler.cpp
@@ -19,6 +19,7 @@
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_random.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
@@ -35,9 +36,10 @@
   DCHECK_EQ(sizeof(kDefaultPasscode), output.size());
   size_t len = std::min(password.GetLength(), output.size());
   size_t remaining = output.size() - len;
-  memcpy(output.data(), password.raw_str(), len);
-  if (remaining)
+  FXSYS_memcpy(output.data(), password.raw_str(), len);
+  if (remaining) {
     memcpy(&output[len], kDefaultPasscode, remaining);
+  }
 }
 
 void CalcEncryptKey(const CPDF_Dictionary* pEncrypt,
diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
index f2c5ac1..cf1a2b5 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -136,8 +136,9 @@
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, PasswordAfterGenerateSave) {
   const char* checksum = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "df9fe67555b7ceb59c99036e8d2c1c76";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "ad97491cab71c02f1f4ef5ba0a7b5593";
+    }
 #if BUILDFLAG(IS_APPLE)
     return "2a308e8cc20a6221112c387d122075a8";
 #else
diff --git a/core/fpdfapi/parser/cpdf_simple_parser.h b/core/fpdfapi/parser/cpdf_simple_parser.h
index e4bf0c0..c953be6 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser.h
+++ b/core/fpdfapi/parser/cpdf_simple_parser.h
@@ -10,7 +10,7 @@
 #include <stdint.h>
 
 #include "core/fxcrt/bytestring.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_SimpleParser {
  public:
diff --git a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
index 965d577..77483b3 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
@@ -7,9 +7,10 @@
 #include <iterator>
 
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(SimpleParserTest, GetWord) {
   static const pdfium::StrFuncTestData test_data[] = {
@@ -57,8 +58,8 @@
     EXPECT_EQ(data.expected_size, word.GetLength()) << " for case " << i;
     if (data.expected_size != word.GetLength())
       continue;
-    EXPECT_EQ(
-        0, memcmp(data.expected, word.unterminated_c_str(), data.expected_size))
+    EXPECT_EQ(0, FXSYS_memcmp(data.expected, word.unterminated_c_str(),
+                              data.expected_size))
         << " for case " << i;
   }
 }
diff --git a/core/fpdfapi/parser/cpdf_stream_acc.h b/core/fpdfapi/parser/cpdf_stream_acc.h
index 1f0e726..74657dd 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc.h
+++ b/core/fpdfapi/parser/cpdf_stream_acc.h
@@ -15,7 +15,7 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Stream;
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.h b/core/fpdfapi/parser/cpdf_syntax_parser.h
index ed79af2..d3f5747 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.h
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.h
@@ -19,7 +19,7 @@
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/weak_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Dictionary;
 class CPDF_IndirectObjectHolder;
diff --git a/core/fpdfapi/parser/fpdf_parser_decode.cpp b/core/fpdfapi/parser/fpdf_parser_decode.cpp
index 6f29b7a..1fbee03 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode.cpp
@@ -8,10 +8,12 @@
 
 #include <ctype.h>
 #include <limits.h>
+#include <stddef.h>
 
 #include <algorithm>
 #include <utility>
 
+#include "build/build_config.h"
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -22,6 +24,7 @@
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/span_util.h"
+#include "core/fxcrt/utf16.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
 
@@ -472,7 +475,7 @@
 }
 
 WideString PDF_DecodeText(pdfium::span<const uint8_t> span) {
-  int dest_pos = 0;
+  size_t dest_pos = 0;
   WideString result;
   if (span.size() >= 2 && ((span[0] == 0xfe && span[1] == 0xff) ||
                            (span[0] == 0xff && span[1] == 0xfe))) {
@@ -485,6 +488,10 @@
         span[0] == 0xfe ? GetUnicodeFromBigEndianBytes
                         : GetUnicodeFromLittleEndianBytes;
     const uint8_t* unicode_str = &span[2];
+
+#if defined(WCHAR_T_IS_UTF32)
+    char16_t high_surrogate = 0;
+#endif  // defined(WCHAR_T_IS_UTF32)
     for (size_t i = 0; i < max_chars * 2; i += 2) {
       uint16_t unicode = GetUnicodeFromBytes(unicode_str + i);
 
@@ -505,8 +512,34 @@
           break;
       }
 
+#if defined(WCHAR_T_IS_UTF32)
+      // TODO(crbug.com/pdfium/2031): Always use UTF-16.
+      if (high_surrogate) {
+        char16_t previous_high_surrogate = high_surrogate;
+        high_surrogate = 0;
+
+        if (pdfium::IsLowSurrogate(unicode)) {
+          dest_buf[dest_pos++] =
+              pdfium::SurrogatePair(previous_high_surrogate, unicode)
+                  .ToCodePoint();
+          continue;
+        }
+        dest_buf[dest_pos++] = previous_high_surrogate;
+      }
+
+      if (pdfium::IsHighSurrogate(unicode)) {
+        high_surrogate = unicode;
+        continue;
+      }
+#endif  // defined(WCHAR_T_IS_UTF32)
       dest_buf[dest_pos++] = unicode;
     }
+
+#if defined(WCHAR_T_IS_UTF32)
+    if (high_surrogate) {
+      dest_buf[dest_pos++] = high_surrogate;
+    }
+#endif  // defined(WCHAR_T_IS_UTF32)
   } else {
     pdfium::span<wchar_t> dest_buf = result.GetBuffer(span.size());
     for (size_t i = 0; i < span.size(); ++i)
@@ -545,18 +578,35 @@
   }
 
   size_t dest_index = 0;
-  size_t encLen = len * 2 + 2;
   {
+#if defined(WCHAR_T_IS_UTF32)
+    // 2 or 4 bytes required per UTF-32 code unit.
     pdfium::span<uint8_t> dest_buf =
-        pdfium::as_writable_bytes(result.GetBuffer(encLen));
+        pdfium::as_writable_bytes(result.GetBuffer(len * 4 + 2));
+#else
+    // 2 bytes required per UTF-16 code unit.
+    pdfium::span<uint8_t> dest_buf =
+        pdfium::as_writable_bytes(result.GetBuffer(len * 2 + 2));
+#endif  // defined(WCHAR_T_IS_UTF32)
+
     dest_buf[dest_index++] = 0xfe;
     dest_buf[dest_index++] = 0xff;
     for (size_t j = 0; j < len; ++j) {
+#if defined(WCHAR_T_IS_UTF32)
+      if (pdfium::IsSupplementary(str[j])) {
+        pdfium::SurrogatePair pair(str[j]);
+        dest_buf[dest_index++] = pair.high() >> 8;
+        dest_buf[dest_index++] = static_cast<uint8_t>(pair.high());
+        dest_buf[dest_index++] = pair.low() >> 8;
+        dest_buf[dest_index++] = static_cast<uint8_t>(pair.low());
+        continue;
+      }
+#endif  // defined(WCHAR_T_IS_UTF32)
       dest_buf[dest_index++] = str[j] >> 8;
       dest_buf[dest_index++] = static_cast<uint8_t>(str[j]);
     }
   }
-  result.ReleaseBuffer(encLen);
+  result.ReleaseBuffer(dest_index);
   return result;
 }
 
diff --git a/core/fpdfapi/parser/fpdf_parser_decode.h b/core/fpdfapi/parser/fpdf_parser_decode.h
index a437455..6e146b5 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.h
+++ b/core/fpdfapi/parser/fpdf_parser_decode.h
@@ -18,7 +18,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
diff --git a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
index 2c2074b..cd3e82a 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
@@ -4,6 +4,9 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <iterator>
 
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -12,9 +15,29 @@
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/string_view_template.h"
+#include "core/fxcrt/widestring.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
+#include "third_party/base/containers/span.h"
+
+namespace {
+
+// Converts a string literal into a `uint8_t` span.
+template <size_t N>
+pdfium::span<const uint8_t> ToSpan(const char (&array)[N]) {
+  return pdfium::span(reinterpret_cast<const uint8_t*>(array), N - 1);
+}
+
+// Converts a string literal into a `ByteString`.
+template <size_t N>
+ByteString ToByteString(const char (&array)[N]) {
+  return ByteString(array, N - 1);
+}
+
+}  // namespace
 
 TEST(ParserDecodeTest, ValidateDecoderPipeline) {
   {
@@ -379,82 +402,102 @@
 }
 
 TEST(ParserDecodeTest, DecodeText) {
-  const struct DecodeTestData {
-    const char* input;
-    size_t input_length;
-    const wchar_t* expected_output;
-    size_t expected_length;
-  } kTestData[] = {
-      // Empty src string.
-      {"", 0, L"", 0},
-      // ASCII text.
-      {"the quick\tfox", 13, L"the quick\tfox", 13},
-      // Unicode text.
-      {"\xFE\xFF\x03\x30\x03\x31", 6, L"\x0330\x0331", 2},
-      // More Unicode text.
-      {"\xFE\xFF\x7F\x51\x98\x75\x00\x20\x56\xFE\x72\x47\x00"
-       "\x20\x8D\x44\x8B\xAF\x66\xF4\x59\x1A\x00\x20\x00\xBB",
-       26,
-       L"\x7F51\x9875\x0020\x56FE\x7247\x0020"
-       L"\x8D44\x8BAF\x66F4\x591A\x0020\x00BB",
-       12},
-      // Unicode escape sequence. https://crbug.com/pdfium/182
-      {"\xFE\xFF\x00\x1B\x6A\x61\x00\x1B\x00\x20\x53\x70\x52\x37", 14,
-       L"\x0020\x5370\x5237", 3},
-      {"\xFE\xFF\x00\x1B\x6A\x61\x00\x1B\x00\x20\x53\x70\x52\x37\x29", 15,
-       L"\x0020\x5370\x5237", 3},
-      {"\xFE\xFF\x00\x1B\x6A\x61\x4A\x50\x00\x1B\x00\x20\x53\x70\x52\x37", 16,
-       L"\x0020\x5370\x5237", 3},
-      {"\xFE\xFF\x00\x20\x00\x1B\x6A\x61\x4A\x50\x00\x1B\x52\x37", 14,
-       L"\x0020\x5237", 2},
-      // https://crbug.com/1001159
-      {"\xFE\xFF\x00\x1B\x00\x1B", 6, L"", 0},
-      {"\xFE\xFF\x00\x1B\x00\x1B\x20", 7, L"", 0},
-      {"\xFE\xFF\x00\x1B\x00\x1B\x00\x20", 8, L"\x0020", 1},
-  };
+  // Empty src string.
+  EXPECT_EQ(L"", PDF_DecodeText(ToSpan("")));
 
-  for (const auto& test_case : kTestData) {
-    WideString output = PDF_DecodeText(
-        pdfium::make_span(reinterpret_cast<const uint8_t*>(test_case.input),
-                          test_case.input_length));
-    ASSERT_EQ(test_case.expected_length, output.GetLength())
-        << "for case " << test_case.input;
-    const wchar_t* str_ptr = output.c_str();
-    for (size_t i = 0; i < test_case.expected_length; ++i) {
-      EXPECT_EQ(test_case.expected_output[i], str_ptr[i])
-          << "for case " << test_case.input << " char " << i;
-    }
-  }
+  // ASCII text.
+  EXPECT_EQ(L"the quick\tfox", PDF_DecodeText(ToSpan("the quick\tfox")));
+
+  // Unicode text.
+  EXPECT_EQ(L"\x0330\x0331",
+            PDF_DecodeText(ToSpan("\xFE\xFF\x03\x30\x03\x31")));
+
+  // More Unicode text.
+  EXPECT_EQ(
+      L"\x7F51\x9875\x0020\x56FE\x7247\x0020"
+      L"\x8D44\x8BAF\x66F4\x591A\x0020\x00BB",
+      PDF_DecodeText(
+          ToSpan("\xFE\xFF\x7F\x51\x98\x75\x00\x20\x56\xFE\x72\x47\x00"
+                 "\x20\x8D\x44\x8B\xAF\x66\xF4\x59\x1A\x00\x20\x00\xBB")));
+
+  // Supplementary Unicode text.
+  EXPECT_EQ(L"🎨", PDF_DecodeText(ToSpan("\xFE\xFF\xD8\x3C\xDF\xA8")));
+}
+
+// https://crbug.com/pdfium/182
+TEST(ParserDecodeTest, DecodeTextWithUnicodeEscapes) {
+  EXPECT_EQ(L"\x0020\x5370\x5237",
+            PDF_DecodeText(ToSpan(
+                "\xFE\xFF\x00\x1B\x6A\x61\x00\x1B\x00\x20\x53\x70\x52\x37")));
+  EXPECT_EQ(
+      L"\x0020\x5370\x5237",
+      PDF_DecodeText(ToSpan(
+          "\xFE\xFF\x00\x1B\x6A\x61\x00\x1B\x00\x20\x53\x70\x52\x37\x29")));
+  EXPECT_EQ(
+      L"\x0020\x5370\x5237",
+      PDF_DecodeText(ToSpan(
+          "\xFE\xFF\x00\x1B\x6A\x61\x4A\x50\x00\x1B\x00\x20\x53\x70\x52\x37")));
+  EXPECT_EQ(L"\x0020\x5237",
+            PDF_DecodeText(ToSpan(
+                "\xFE\xFF\x00\x20\x00\x1B\x6A\x61\x4A\x50\x00\x1B\x52\x37")));
+}
+
+// https://crbug.com/1001159
+TEST(ParserDecodeTest, DecodeTextWithInvalidUnicodeEscapes) {
+  EXPECT_EQ(L"", PDF_DecodeText(ToSpan("\xFE\xFF\x00\x1B\x00\x1B")));
+  EXPECT_EQ(L"", PDF_DecodeText(ToSpan("\xFE\xFF\x00\x1B\x00\x1B\x20")));
+  EXPECT_EQ(L"\x0020",
+            PDF_DecodeText(ToSpan("\xFE\xFF\x00\x1B\x00\x1B\x00\x20")));
+}
+
+TEST(ParserDecodeTest, DecodeTextWithUnpairedSurrogates) {
+  EXPECT_EQ(L"\xD800", PDF_DecodeText(ToSpan("\xFE\xFF\xD8\x00"))) << "High";
+  EXPECT_EQ(L"\xDC00", PDF_DecodeText(ToSpan("\xFE\xFF\xDC\x00"))) << "Low";
+  EXPECT_EQ(L"\xD800🎨",
+            PDF_DecodeText(ToSpan("\xFE\xFF\xD8\x00\xD8\x3C\xDF\xA8")))
+      << "High-high";
+  EXPECT_EQ(L"🎨\xDC00",
+            PDF_DecodeText(ToSpan("\xFE\xFF\xD8\x3C\xDF\xA8\xDC\x00")))
+      << "Low-low";
 }
 
 TEST(ParserDecodeTest, EncodeText) {
-  const struct EncodeTestData {
-    const wchar_t* input;
-    const char* expected_output;
-    size_t expected_length;
-  } kTestData[] = {
-      // Empty src string.
-      {L"", "", 0},
-      // ASCII text.
-      {L"the quick\tfox", "the quick\tfox", 13},
-      // Unicode text.
-      {L"\x0330\x0331", "\xFE\xFF\x03\x30\x03\x31", 6},
-      // More Unicode text.
-      {L"\x7F51\x9875\x0020\x56FE\x7247\x0020"
-       L"\x8D44\x8BAF\x66F4\x591A\x0020\x00BB",
-       "\xFE\xFF\x7F\x51\x98\x75\x00\x20\x56\xFE\x72\x47\x00"
-       "\x20\x8D\x44\x8B\xAF\x66\xF4\x59\x1A\x00\x20\x00\xBB",
-       26},
-  };
+  // Empty src string.
+  EXPECT_EQ("", PDF_EncodeText(L""));
 
-  for (const auto& test_case : kTestData) {
-    ByteString output = PDF_EncodeText(test_case.input);
-    ASSERT_EQ(test_case.expected_length, output.GetLength())
-        << "for case " << test_case.input;
-    const char* str_ptr = output.c_str();
-    for (size_t j = 0; j < test_case.expected_length; ++j) {
-      EXPECT_EQ(test_case.expected_output[j], str_ptr[j])
-          << "for case " << test_case.input << " char " << j;
+  // ASCII text.
+  EXPECT_EQ("the quick\tfox", PDF_EncodeText(L"the quick\tfox"));
+
+  // Unicode text.
+  EXPECT_EQ("\xFE\xFF\x03\x30\x03\x31", PDF_EncodeText(L"\x0330\x0331"));
+
+  // More Unicode text.
+  EXPECT_EQ(
+      ToByteString("\xFE\xFF\x7F\x51\x98\x75\x00\x20\x56\xFE\x72\x47\x00"
+                   "\x20\x8D\x44\x8B\xAF\x66\xF4\x59\x1A\x00\x20\x00\xBB"),
+      PDF_EncodeText(L"\x7F51\x9875\x0020\x56FE\x7247\x0020"
+                     L"\x8D44\x8BAF\x66F4\x591A\x0020\x00BB"));
+
+  // Supplementary Unicode text.
+  EXPECT_EQ("\xFE\xFF\xD8\x3C\xDF\xA8", PDF_EncodeText(L"🎨"));
+}
+
+TEST(ParserDecodeTest, RoundTripText) {
+  for (int pdf_code_point = 0; pdf_code_point < 256; ++pdf_code_point) {
+    ByteString original(static_cast<char>(pdf_code_point));
+    ByteString reencoded =
+        PDF_EncodeText(PDF_DecodeText(original.raw_span()).AsStringView());
+
+    switch (pdf_code_point) {
+      case 0x7F:
+      case 0x9F:
+      case 0xAD:
+        EXPECT_EQ(ByteString('\0'), reencoded) << "PDFDocEncoding undefined";
+        break;
+
+      default:
+        EXPECT_EQ(original, reencoded) << "PDFDocEncoding: " << pdf_code_point;
+        break;
     }
   }
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_utility.cpp b/core/fpdfapi/parser/fpdf_parser_utility.cpp
index 950753e..c2f626a 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_utility.cpp
@@ -21,14 +21,13 @@
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_stream.h"
 #include "third_party/base/check.h"
-#include "third_party/base/notreached.h"
 
 // Indexed by 8-bit character code, contains either:
 //   'W' - for whitespace: NUL, TAB, CR, LF, FF, SPACE, 0x80, 0xff
 //   'N' - for numeric: 0123456789+-.
 //   'D' - for delimiter: %()/<>[]{}
 //   'R' - otherwise.
-const char PDF_CharType[256] = {
+const char kPDFCharTypes[256] = {
     // NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL  BS   HT   LF   VT   FF   CR   SO
     // SI
     'W', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W', 'W', 'R', 'W', 'W', 'R',
@@ -253,9 +252,6 @@
       buf << "\r\nendstream";
       break;
     }
-    default:
-      NOTREACHED();
-      break;
   }
   return buf;
 }
diff --git a/core/fpdfapi/parser/fpdf_parser_utility.h b/core/fpdfapi/parser/fpdf_parser_utility.h
index 259495e..521e20a 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.h
+++ b/core/fpdfapi/parser/fpdf_parser_utility.h
@@ -19,20 +19,20 @@
 class CPDF_Object;
 class IFX_SeekableReadStream;
 
-// Use the accessors below instead of directly accessing PDF_CharType.
-extern const char PDF_CharType[256];
+// Use the accessors below instead of directly accessing kPDFCharTypes.
+extern const char kPDFCharTypes[256];
 
 inline bool PDFCharIsWhitespace(uint8_t c) {
-  return PDF_CharType[c] == 'W';
+  return kPDFCharTypes[c] == 'W';
 }
 inline bool PDFCharIsNumeric(uint8_t c) {
-  return PDF_CharType[c] == 'N';
+  return kPDFCharTypes[c] == 'N';
 }
 inline bool PDFCharIsDelimiter(uint8_t c) {
-  return PDF_CharType[c] == 'D';
+  return kPDFCharTypes[c] == 'D';
 }
 inline bool PDFCharIsOther(uint8_t c) {
-  return PDF_CharType[c] == 'R';
+  return kPDFCharTypes[c] == 'R';
 }
 
 inline bool PDFCharIsLineEnding(uint8_t c) {
diff --git a/core/fpdfapi/parser/object_tree_traversal_util.cpp b/core/fpdfapi/parser/object_tree_traversal_util.cpp
index e1a0d52..d319484 100644
--- a/core/fpdfapi/parser/object_tree_traversal_util.cpp
+++ b/core/fpdfapi/parser/object_tree_traversal_util.cpp
@@ -17,6 +17,7 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
 
@@ -80,7 +81,6 @@
           const CPDF_Reference* ref_object = current_object->AsReference();
           const uint32_t ref_object_number = GetObjectNumber(ref_object);
           const uint32_t referenced_object_number = ref_object->GetRefObjNum();
-          CHECK(referenced_object_number);
 
           RetainPtr<const CPDF_Object> referenced_object;
           if (ref_object->HasIndirectObjectHolder()) {
@@ -93,6 +93,7 @@
           }
           // Unlike the other object types, CPDF_Reference can point at nullptr.
           if (referenced_object) {
+            CHECK(referenced_object_number);
             reference_entries.push_back(
                 {ref_object_number, referenced_object_number});
             PushNewObject(ref_object, referenced_object);
@@ -169,7 +170,7 @@
     return it != object_number_map_.end() ? it->second : 0;
   }
 
-  const CPDF_Document* const document_;
+  UnownedPtr<const CPDF_Document> const document_;
 
   // Queue of objects to traverse.
   // - Pointers in the queue are non-null.
diff --git a/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp
index c90c77e..ed0c8e1 100644
--- a/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp
+++ b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp
@@ -64,6 +64,14 @@
 }
 
 TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithReferencesObjectZero) {
+  ASSERT_TRUE(OpenDocument("rectangles_object_zero.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<uint32_t> referenced_objects = GetObjectsWithReferences(doc);
+  EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({1, 2, 3, 4}));
+}
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
        GetObjectsWithMultipleReferencesBasic) {
   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
   CPDF_Document* doc = GetCPDFDocument(document());
@@ -94,3 +102,11 @@
   std::set<uint32_t> referenced_objects = GetObjectsWithMultipleReferences(doc);
   EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({5, 6, 7}));
 }
+
+TEST_F(ObjectTreeTraversalUtilEmbedderTest,
+       GetObjectsWithMultipleReferencesObjectZero) {
+  ASSERT_TRUE(OpenDocument("rectangles_object_zero.pdf"));
+  CPDF_Document* doc = GetCPDFDocument(document());
+  std::set<uint32_t> referenced_objects = GetObjectsWithMultipleReferences(doc);
+  EXPECT_TRUE(referenced_objects.empty());
+}
diff --git a/core/fpdfapi/render/charposlist.h b/core/fpdfapi/render/charposlist.h
index af0ea6d..47c5d57 100644
--- a/core/fpdfapi/render/charposlist.h
+++ b/core/fpdfapi/render/charposlist.h
@@ -11,7 +11,7 @@
 
 #include <vector>
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Font;
 class TextCharPos;
diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp
index c5f3f1d..a773cd2 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp
+++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp
@@ -40,11 +40,6 @@
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "core/fxge/dib/cfx_imagetransformer.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
-
-#if defined(_SKIA_SUPPORT_)
-#include "core/fxge/skia/fx_skia_device.h"
-#endif
 
 namespace {
 
@@ -67,19 +62,6 @@
   bitmap_device.GetBitmap()->Clear(color);
 }
 
-RetainPtr<CFX_DIBBase> PreMultiplyBitmapIfAlpha(
-    RetainPtr<CFX_DIBBase> base_bitmap) {
-#if defined(_SKIA_SUPPORT_)
-  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
-    RetainPtr<CFX_DIBitmap> premultiplied = base_bitmap->Realize();
-    if (base_bitmap->IsAlphaFormat())
-      premultiplied->PreMultiply();
-    return premultiplied;
-  }
-#endif  // defined(_SKIA_SUPPORT_)
-  return base_bitmap;
-}
-
 }  // namespace
 
 CPDF_ImageRenderer::CPDF_ImageRenderer(CPDF_RenderStatus* pStatus)
@@ -283,11 +265,11 @@
         continue;
       }
       int orig = (*dest_scan - matte_b) * 255 / alpha + matte_b;
-      *dest_scan++ = pdfium::clamp(orig, 0, 255);
+      *dest_scan++ = std::clamp(orig, 0, 255);
       orig = (*dest_scan - matte_g) * 255 / alpha + matte_g;
-      *dest_scan++ = pdfium::clamp(orig, 0, 255);
+      *dest_scan++ = std::clamp(orig, 0, 255);
       orig = (*dest_scan - matte_r) * 255 / alpha + matte_r;
-      *dest_scan++ = pdfium::clamp(orig, 0, 255);
+      *dest_scan++ = std::clamp(orig, 0, 255);
       dest_scan++;
     }
   }
@@ -381,6 +363,8 @@
   }
   CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pLoader->GetMask(),
                      new_matrix, rect);
+  DCHECK(!bitmap_device2.GetBitmap()->HasPalette());
+  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask);
 #if defined(_SKIA_SUPPORT_)
   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
     m_pRenderStatus->GetRenderDevice()->SetBitsWithMask(
@@ -389,7 +373,6 @@
     return false;
   }
 #endif
-  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask);
   bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
   if (m_BitmapAlpha < 255)
     bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha);
@@ -412,10 +395,9 @@
       m_ResampleOptions.bInterpolateBilinear = true;
     }
   }
-  RetainPtr<CFX_DIBBase> bitmap = PreMultiplyBitmapIfAlpha(m_pDIBBase);
   if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
-          bitmap, m_BitmapAlpha, m_FillArgb, m_ImageMatrix, m_ResampleOptions,
-          &m_DeviceHandle, m_BlendType)) {
+          m_pDIBBase, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
+          m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
     if (m_DeviceHandle) {
       m_Mode = Mode::kBlend;
       return true;
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.h b/core/fpdfapi/render/cpdf_progressiverenderer.h
index 67170fc..7af65a0 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.h
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.h
@@ -53,7 +53,7 @@
   std::unique_ptr<CPDF_RenderStatus> m_pRenderStatus;
   CFX_FloatRect m_ClipRect;
   uint32_t m_LayerIndex = 0;
-  CPDF_RenderContext::Layer* m_pCurrentLayer = nullptr;
+  UnownedPtr<CPDF_RenderContext::Layer> m_pCurrentLayer;
   CPDF_PageObjectHolder::const_iterator m_LastObjectRendered;
 };
 
diff --git a/core/fpdfapi/render/cpdf_rendershading.cpp b/core/fpdfapi/render/cpdf_rendershading.cpp
index ce7543b..bce967f 100644
--- a/core/fpdfapi/render/cpdf_rendershading.cpp
+++ b/core/fpdfapi/render/cpdf_rendershading.cpp
@@ -27,6 +27,7 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/span_util.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_path.h"
@@ -34,8 +35,7 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
@@ -390,8 +390,8 @@
       end_index = 0;
     }
 
-    int start_x = pdfium::clamp(min_x, 0, pBitmap->GetWidth());
-    int end_x = pdfium::clamp(max_x, 0, pBitmap->GetWidth());
+    int start_x = std::clamp(min_x, 0, pBitmap->GetWidth());
+    int end_x = std::clamp(max_x, 0, pBitmap->GetWidth());
     float r_unit = (r[end_index] - r[start_index]) / (max_x - min_x);
     float g_unit = (g[end_index] - g[start_index]) / (max_x - min_x);
     float b_unit = (b[end_index] - b[start_index]) / (max_x - min_x);
@@ -767,7 +767,7 @@
 
   int max_delta;
   CFX_Path path;
-  CFX_RenderDevice* pDevice;
+  UnownedPtr<CFX_RenderDevice> pDevice;
   int bNoPathSmooth;
   int alpha;
   CoonColor patch_colors[4];
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index fe58de8..39101f0 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -69,12 +69,8 @@
 #include "core/fxge/text_glyph_pos.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
-
-#if defined(_SKIA_SUPPORT_)
-#include "core/fxge/skia/fx_skia_device.h"
-#endif
 
 namespace {
 
@@ -1213,10 +1209,6 @@
         }
         pDIBitmap->MultiplyAlpha(bitmap_alpha);
       }
-#if defined(_SKIA_SUPPORT_)
-      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        pDIBitmap->PreMultiply();
-#endif
       if (m_pDevice->SetDIBits(pDIBitmap, left, top)) {
         return;
       }
@@ -1375,7 +1367,7 @@
   if (!pMask->Create(width, height, FXDIB_Format::k8bppMask))
     return nullptr;
 
-  pdfium::span<uint8_t> dest_buf = pMask->GetBuffer();
+  pdfium::span<uint8_t> dest_buf = pMask->GetWritableBuffer();
   pdfium::span<const uint8_t> src_buf = bitmap->GetBuffer();
   int dest_pitch = pMask->GetPitch();
   int src_pitch = bitmap->GetPitch();
diff --git a/core/fpdfapi/render/cpdf_rendertiling.cpp b/core/fpdfapi/render/cpdf_rendertiling.cpp
index fcde2b2..5f9dcd7 100644
--- a/core/fpdfapi/render/cpdf_rendertiling.cpp
+++ b/core/fpdfapi/render/cpdf_rendertiling.cpp
@@ -18,6 +18,7 @@
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 
 namespace {
 
diff --git a/core/fpdfapi/render/cpdf_rendertiling.h b/core/fpdfapi/render/cpdf_rendertiling.h
index b687f74..34fb56b 100644
--- a/core/fpdfapi/render/cpdf_rendertiling.h
+++ b/core/fpdfapi/render/cpdf_rendertiling.h
@@ -8,8 +8,8 @@
 #define CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
 
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
 
+class CFX_DIBitmap;
 class CFX_Matrix;
 class CPDF_Form;
 class CPDF_PageObject;
diff --git a/core/fpdfapi/render/cpdf_textrenderer.h b/core/fpdfapi/render/cpdf_textrenderer.h
index b5529a6..857340f 100644
--- a/core/fpdfapi/render/cpdf_textrenderer.h
+++ b/core/fpdfapi/render/cpdf_textrenderer.h
@@ -12,7 +12,7 @@
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_RenderDevice;
 class CFX_GraphStateData;
diff --git a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
index a51e16e..428c56b 100644
--- a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
+++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
@@ -24,11 +24,14 @@
 constexpr FX_ARGB kWhite = 0xFFFFFFFF;
 
 const char* AnnotationStampWithApBaseContentChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "4fedc838daa6762cf7eee180986a0f1b";
+  }
 #if BUILDFLAG(IS_APPLE)
-  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "243f3d6267d9db09198fed9f8c4957fd";
-#endif
+  return "243f3d6267d9db09198fed9f8c4957fd";
+#else
   return "e31414933c9ff3950773981e5bf61678";
+#endif
 }
 
 }  // namespace
@@ -313,7 +316,7 @@
   // Test rendering of text with forced color scheme on.
   const char* content_with_text_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "5ece6059efdc2ecb2894fa3cf329dc94";
+      return "edd919ec8b59fab1f16b5f2adb1175f3";
 #if BUILDFLAG(IS_APPLE)
     return "ee4ec12f54ce8d117a73bd9b85a8954d";
 #else
@@ -369,7 +372,7 @@
   // Normal blend mode.
   const char* content_with_highlight_fill_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "9b6273fdbc9db780c49f7540756209f8";
+      return "49dcfcfdc38d200bb3d57a2ca3086034";
 #if BUILDFLAG(IS_APPLE)
     return "a820afec9b99d3d3f2e9e9382bbad7c1";
 #else
@@ -396,7 +399,7 @@
 
   const char* md5_content_with_highlight = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "772246195d18f75d40a22bee913c098f";
+      return "c609e8810fba2f12db8f8a2b043d97bd";
 #if BUILDFLAG(IS_APPLE)
     return "8837bea0b3520164b1784e513c882a2d";
 #else
@@ -415,8 +418,13 @@
 TEST_F(FPDFProgressiveRenderEmbedderTest, RenderInkWithColorScheme) {
   // Test rendering of multiple ink with forced color scheme on.
   const char* content_with_ink_checksum = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "ebc57721e4c8da34156e09b9b2e62fb0";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+      return "5108aa537b6ecc37b3f0a35b76c1b379";
+#else
+      return "b39d9f68ff71963d82c43eb20caa8f4d";
+#endif
+    }
     return "797bce7dc6c50ee86b095405df9fe5aa";
   }();
 
@@ -431,7 +439,7 @@
   // Test rendering of static annotation with forced color scheme on.
   const char* content_with_stamp_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "a791fdb4f595bb6c4187cc2aeed5e9e8";
+      return "6e028012a4854ebfd9ee92da862bf679";
 #if BUILDFLAG(IS_APPLE)
     return "8170c539e95f22f14eb8f266a5f1bbed";
 #else
diff --git a/core/fpdfdoc/BUILD.gn b/core/fpdfdoc/BUILD.gn
index 2e7a150..5e11adb 100644
--- a/core/fpdfdoc/BUILD.gn
+++ b/core/fpdfdoc/BUILD.gn
@@ -97,6 +97,7 @@
   sources = [
     "cpdf_action_unittest.cpp",
     "cpdf_annot_unittest.cpp",
+    "cpdf_annotlist_unittest.cpp",
     "cpdf_bafontmap_unittest.cpp",
     "cpdf_defaultappearance_unittest.cpp",
     "cpdf_dest_unittest.cpp",
diff --git a/core/fpdfdoc/cpdf_annotlist.cpp b/core/fpdfdoc/cpdf_annotlist.cpp
index f565a5a..ff3ed81 100644
--- a/core/fpdfdoc/cpdf_annotlist.cpp
+++ b/core/fpdfdoc/cpdf_annotlist.cpp
@@ -23,6 +23,7 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
@@ -64,7 +65,6 @@
     case CPDF_Annot::Subtype::THREED:
     case CPDF_Annot::Subtype::RICHMEDIA:
     case CPDF_Annot::Subtype::XFAWIDGET:
-    default:
       return false;
   }
 }
@@ -79,12 +79,13 @@
   if (!pParentDict)
     return nullptr;
 
-  // TODO(jaepark): We shouldn't strip BOM for some strings and not for others.
-  // See pdfium:593.
-  WideString sContents =
-      pParentDict->GetUnicodeTextFor(pdfium::annotation::kContents);
-  if (sContents.IsEmpty())
+  // TODO(crbug.com/pdfium/1098): Determine if we really need to check if
+  // /Contents is empty or not. If so, optimize decoding for empty check.
+  ByteString contents =
+      pParentDict->GetByteStringFor(pdfium::annotation::kContents);
+  if (PDF_DecodeText(contents.raw_span()).IsEmpty()) {
     return nullptr;
+  }
 
   auto pAnnotDict = pDocument->New<CPDF_Dictionary>();
   pAnnotDict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "Annot");
@@ -92,8 +93,8 @@
   pAnnotDict->SetNewFor<CPDF_String>(
       pdfium::form_fields::kT,
       pParentDict->GetByteStringFor(pdfium::form_fields::kT), false);
-  pAnnotDict->SetNewFor<CPDF_String>(pdfium::annotation::kContents,
-                                     sContents.ToUTF8(), false);
+  pAnnotDict->SetNewFor<CPDF_String>(pdfium::annotation::kContents, contents,
+                                     /*bHex=*/false);
 
   CFX_FloatRect rect = pParentDict->GetRectFor(pdfium::annotation::kRect);
   rect.Normalize();
diff --git a/core/fpdfdoc/cpdf_annotlist_unittest.cpp b/core/fpdfdoc/cpdf_annotlist_unittest.cpp
new file mode 100644
index 0000000..653fdff
--- /dev/null
+++ b/core/fpdfdoc/cpdf_annotlist_unittest.cpp
@@ -0,0 +1,125 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfdoc/cpdf_annotlist.h"
+
+#include <stdint.h>
+
+#include <initializer_list>
+#include <memory>
+
+#include "constants/annotation_common.h"
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/test_with_page_module.h"
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/parser/cpdf_test_document.h"
+#include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/widestring.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class CPDFAnnotListTest : public TestWithPageModule {
+ public:
+  void SetUp() override {
+    TestWithPageModule::SetUp();
+
+    document_ = std::make_unique<CPDF_TestDocument>();
+    document_->SetRoot(pdfium::MakeRetain<CPDF_Dictionary>());
+    page_ = pdfium::MakeRetain<CPDF_Page>(
+        document_.get(), pdfium::MakeRetain<CPDF_Dictionary>());
+  }
+
+  void TearDown() override {
+    page_.Reset();
+    document_.reset();
+
+    TestWithPageModule::TearDown();
+  }
+
+ protected:
+  void AddTextAnnotation(const ByteString& contents) {
+    RetainPtr<CPDF_Dictionary> annotation =
+        page_->GetOrCreateAnnotsArray()->AppendNew<CPDF_Dictionary>();
+    annotation->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Text");
+    annotation->SetNewFor<CPDF_String>(pdfium::annotation::kContents, contents,
+                                       /*bHex=*/false);
+  }
+
+  std::unique_ptr<CPDF_TestDocument> document_;
+  RetainPtr<CPDF_Page> page_;
+};
+
+ByteString MakeByteString(std::initializer_list<uint8_t> bytes) {
+  return ByteString(std::data(bytes), std::size(bytes));
+}
+
+ByteString GetRawContents(const CPDF_Annot* annotation) {
+  return annotation->GetAnnotDict()->GetByteStringFor(
+      pdfium::annotation::kContents);
+}
+
+WideString GetDecodedContents(const CPDF_Annot* annotation) {
+  return annotation->GetAnnotDict()->GetUnicodeTextFor(
+      pdfium::annotation::kContents);
+}
+
+}  // namespace
+
+TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromPdfEncoded) {
+  const ByteString kContents = MakeByteString({'A', 'a', 0xE4, 0xA0});
+  AddTextAnnotation(kContents);
+
+  CPDF_AnnotList list(page_);
+
+  ASSERT_EQ(2u, list.Count());
+  EXPECT_EQ(kContents, GetRawContents(list.GetAt(1)));
+  EXPECT_EQ(WideString::FromUTF8("Aaä€"), GetDecodedContents(list.GetAt(1)));
+}
+
+TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromUnicode) {
+  const ByteString kContents =
+      MakeByteString({0xFE, 0xFF, 0x00, 'A', 0x00, 'a', 0x00, 0xE4, 0x20, 0xAC,
+                      0xD8, 0x3C, 0xDF, 0xA8});
+  AddTextAnnotation(kContents);
+
+  CPDF_AnnotList list(page_);
+
+  ASSERT_EQ(2u, list.Count());
+  EXPECT_EQ(kContents, GetRawContents(list.GetAt(1)));
+
+  EXPECT_EQ(WideString::FromUTF8("Aaä€🎨"), GetDecodedContents(list.GetAt(1)));
+}
+
+TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromEmptyPdfEncoded) {
+  AddTextAnnotation("");
+
+  CPDF_AnnotList list(page_);
+
+  EXPECT_EQ(1u, list.Count());
+}
+
+TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromEmptyUnicode) {
+  const ByteString kContents = MakeByteString({0xFE, 0xFF});
+  AddTextAnnotation(kContents);
+
+  CPDF_AnnotList list(page_);
+
+  EXPECT_EQ(1u, list.Count());
+}
+
+TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromEmptyUnicodedWithEscape) {
+  const ByteString kContents =
+      MakeByteString({0xFE, 0xFF, 0x00, 0x1B, 'j', 'a', 0x00, 0x1B});
+  AddTextAnnotation(kContents);
+
+  CPDF_AnnotList list(page_);
+
+  EXPECT_EQ(1u, list.Count());
+}
diff --git a/core/fpdfdoc/cpdf_defaultappearance.cpp b/core/fpdfdoc/cpdf_defaultappearance.cpp
index 1eda1d9..e3b91a9 100644
--- a/core/fpdfdoc/cpdf_defaultappearance.cpp
+++ b/core/fpdfdoc/cpdf_defaultappearance.cpp
@@ -84,24 +84,23 @@
   if (m_csDA.IsEmpty())
     return absl::nullopt;
 
-  float fc[4];
   CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span());
   if (FindTagParamFromStart(&syntax, "g", 1)) {
-    fc[0] = StringToFloat(syntax.GetWord());
-    return CFX_Color(CFX_Color::Type::kGray, fc[0]);
+    float gray = StringToFloat(syntax.GetWord());
+    return CFX_Color(CFX_Color::Type::kGray, gray);
   }
   if (FindTagParamFromStart(&syntax, "rg", 3)) {
-    fc[0] = StringToFloat(syntax.GetWord());
-    fc[1] = StringToFloat(syntax.GetWord());
-    fc[2] = StringToFloat(syntax.GetWord());
-    return CFX_Color(CFX_Color::Type::kRGB, fc[0], fc[1], fc[2]);
+    float r = StringToFloat(syntax.GetWord());
+    float g = StringToFloat(syntax.GetWord());
+    float b = StringToFloat(syntax.GetWord());
+    return CFX_Color(CFX_Color::Type::kRGB, r, g, b);
   }
   if (FindTagParamFromStart(&syntax, "k", 4)) {
-    fc[0] = StringToFloat(syntax.GetWord());
-    fc[1] = StringToFloat(syntax.GetWord());
-    fc[2] = StringToFloat(syntax.GetWord());
-    fc[3] = StringToFloat(syntax.GetWord());
-    return CFX_Color(CFX_Color::Type::kCMYK, fc[0], fc[1], fc[2], fc[3]);
+    float c = StringToFloat(syntax.GetWord());
+    float m = StringToFloat(syntax.GetWord());
+    float y = StringToFloat(syntax.GetWord());
+    float k = StringToFloat(syntax.GetWord());
+    return CFX_Color(CFX_Color::Type::kCMYK, c, m, y, k);
   }
   return absl::nullopt;
 }
@@ -135,8 +134,7 @@
                    static_cast<int>(g * 255 + 0.5f),
                    static_cast<int>(b * 255 + 0.5f)));
   }
-  NOTREACHED();
-  return absl::nullopt;
+  NOTREACHED_NORETURN();
 }
 
 // static
diff --git a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
index 86bac27..ce08bf3 100644
--- a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
+++ b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
@@ -9,7 +9,7 @@
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(CPDFDefaultAppearanceTest, FindTagParamFromStart) {
   static const struct FindTagTestStruct {
diff --git a/core/fpdfdoc/cpdf_generateap.cpp b/core/fpdfdoc/cpdf_generateap.cpp
index 8c6f25e..234da4c 100644
--- a/core/fpdfdoc/cpdf_generateap.cpp
+++ b/core/fpdfdoc/cpdf_generateap.cpp
@@ -218,7 +218,6 @@
   if (fWidth > 0.0f) {
     float fHalfWidth = fWidth / 2.0f;
     switch (nStyle) {
-      default:
       case BorderStyle::kSolid:
         sColor = GenerateColorAP(color, PaintOperation::kFill);
         if (sColor.GetLength() > 0) {
diff --git a/core/fpdfdoc/cpdf_interactiveform.h b/core/fpdfdoc/cpdf_interactiveform.h
index 3eb3c30..3b6e4f1 100644
--- a/core/fpdfdoc/cpdf_interactiveform.h
+++ b/core/fpdfdoc/cpdf_interactiveform.h
@@ -23,7 +23,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFieldTree;
 class CFDF_Document;
diff --git a/core/fpdfdoc/cpdf_metadata.cpp b/core/fpdfdoc/cpdf_metadata.cpp
index 228a0c1..31564f4 100644
--- a/core/fpdfdoc/cpdf_metadata.cpp
+++ b/core/fpdfdoc/cpdf_metadata.cpp
@@ -20,8 +20,15 @@
 
 namespace {
 
-void CheckForSharedFormInternal(CFX_XMLElement* element,
+constexpr int kMaxMetaDataDepth = 128;
+
+bool CheckForSharedFormInternal(int depth,
+                                CFX_XMLElement* element,
                                 std::vector<UnsupportedFeature>* unsupported) {
+  if (depth >= kMaxMetaDataDepth) {
+    return false;
+  }
+
   WideString attr =
       element->GetAttribute(WideString::FromASCII("xmlns:adhocwf"));
   if (attr.EqualsASCII("http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/")) {
@@ -54,10 +61,13 @@
 
   for (auto* child = element->GetFirstChild(); child;
        child = child->GetNextSibling()) {
-    CFX_XMLElement* pElement = ToXMLElement(child);
-    if (pElement)
-      CheckForSharedFormInternal(pElement, unsupported);
+    CFX_XMLElement* xml_element = ToXMLElement(child);
+    if (xml_element &&
+        !CheckForSharedFormInternal(depth + 1, xml_element, unsupported)) {
+      return false;
+    }
   }
+  return true;
 }
 
 }  // namespace
@@ -80,6 +90,6 @@
     return {};
 
   std::vector<UnsupportedFeature> unsupported;
-  CheckForSharedFormInternal(doc->GetRoot(), &unsupported);
+  CheckForSharedFormInternal(/*depth=*/0, doc->GetRoot(), &unsupported);
   return unsupported;
 }
diff --git a/core/fpdfdoc/cpdf_metadata_unittest.cpp b/core/fpdfdoc/cpdf_metadata_unittest.cpp
index cfcb8f3..4bd30ed 100644
--- a/core/fpdfdoc/cpdf_metadata_unittest.cpp
+++ b/core/fpdfdoc/cpdf_metadata_unittest.cpp
@@ -106,6 +106,39 @@
   EXPECT_EQ(0U, results.size());
 }
 
+TEST(CPDF_MetadataTest, CheckSharedFormExceedMaxDepth) {
+  // Node <parent> has the depth of 130, which exceeds the maximum node depth of
+  // `kMaxMetaDataDepth`.
+  static const char kData[] =
+      "<?xml charset=\"utf-8\"?>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<node><node><node><node><node><node><node><node><node><node>\n"
+      "<parent>\n"
+      "<node xmlns:adhocwf=\"http://ns.adobe.com/AcrobatAdhocWorkflow/1.0/\">\n"
+      "<adhocwf:workflowType>0</adhocwf:workflowType>\n"
+      "<adhocwf:version>1.1</adhocwf:version>\n"
+      "</node>"
+      "</parent>";
+
+  auto stream = pdfium::MakeRetain<CPDF_Stream>();
+  stream->SetData(ByteStringView(kData).raw_span());
+  CPDF_Metadata metadata(stream);
+
+  auto results = metadata.CheckForSharedForm();
+  ASSERT_EQ(0U, results.size());
+}
+
 TEST(CPDF_MetadataTest, CheckSharedFormWrongNamespace) {
   static const char data[] =
       "<?xml charset=\"utf-8\"?>\n"
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index 3b60fab..95d540f 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -18,7 +18,7 @@
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcrt/stl_util.h"
 #include "third_party/base/check.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
diff --git a/core/fpdfdoc/cpdf_structelement.cpp b/core/fpdfdoc/cpdf_structelement.cpp
index b28774d..95e887f 100644
--- a/core/fpdfdoc/cpdf_structelement.cpp
+++ b/core/fpdfdoc/cpdf_structelement.cpp
@@ -29,7 +29,7 @@
     : m_pTree(pTree),
       m_pDict(std::move(pDict)),
       m_Type(m_pTree->GetRoleMapNameFor(m_pDict->GetNameFor("S"))) {
-  LoadKids(m_pDict);
+  LoadKids();
 }
 
 CPDF_StructElement::~CPDF_StructElement() {
@@ -99,11 +99,11 @@
   return bSave;
 }
 
-void CPDF_StructElement::LoadKids(RetainPtr<const CPDF_Dictionary> pDict) {
-  RetainPtr<const CPDF_Object> pObj = pDict->GetObjectFor("Pg");
+void CPDF_StructElement::LoadKids() {
+  RetainPtr<const CPDF_Object> pObj = m_pDict->GetObjectFor("Pg");
   const CPDF_Reference* pRef = ToReference(pObj.Get());
-  const uint32_t PageObjNum = pRef ? pRef->GetRefObjNum() : 0;
-  RetainPtr<const CPDF_Object> pKids = pDict->GetDirectObjectFor("K");
+  const uint32_t page_obj_num = pRef ? pRef->GetRefObjNum() : 0;
+  RetainPtr<const CPDF_Object> pKids = m_pDict->GetDirectObjectFor("K");
   if (!pKids)
     return;
 
@@ -111,28 +111,29 @@
   if (const CPDF_Array* pArray = pKids->AsArray()) {
     m_Kids.resize(pArray->size());
     for (size_t i = 0; i < pArray->size(); ++i) {
-      LoadKid(PageObjNum, pArray->GetDirectObjectAt(i), &m_Kids[i]);
+      LoadKid(page_obj_num, pArray->GetDirectObjectAt(i), m_Kids[i]);
     }
     return;
   }
 
   m_Kids.resize(1);
-  LoadKid(PageObjNum, std::move(pKids), &m_Kids[0]);
+  LoadKid(page_obj_num, std::move(pKids), m_Kids[0]);
 }
 
-void CPDF_StructElement::LoadKid(uint32_t PageObjNum,
+void CPDF_StructElement::LoadKid(uint32_t page_obj_num,
                                  RetainPtr<const CPDF_Object> pKidObj,
-                                 Kid* pKid) {
+                                 Kid& kid) {
   if (!pKidObj)
     return;
 
   if (pKidObj->IsNumber()) {
-    if (m_pTree->GetPageObjNum() != PageObjNum)
+    if (m_pTree->GetPageObjNum() != page_obj_num) {
       return;
+    }
 
-    pKid->m_Type = Kid::kPageContent;
-    pKid->m_ContentId = pKidObj->GetInteger();
-    pKid->m_PageObjNum = PageObjNum;
+    kid.m_Type = Kid::kPageContent;
+    kid.m_ContentId = pKidObj->GetInteger();
+    kid.m_PageObjNum = page_obj_num;
     return;
   }
 
@@ -142,33 +143,33 @@
 
   if (RetainPtr<const CPDF_Reference> pRef =
           ToReference(pKidDict->GetObjectFor("Pg"))) {
-    PageObjNum = pRef->GetRefObjNum();
+    page_obj_num = pRef->GetRefObjNum();
   }
   ByteString type = pKidDict->GetNameFor("Type");
   if ((type == "MCR" || type == "OBJR") &&
-      m_pTree->GetPageObjNum() != PageObjNum) {
+      m_pTree->GetPageObjNum() != page_obj_num) {
     return;
   }
 
   if (type == "MCR") {
-    pKid->m_Type = Kid::kStreamContent;
+    kid.m_Type = Kid::kStreamContent;
     RetainPtr<const CPDF_Reference> pRef =
         ToReference(pKidDict->GetObjectFor("Stm"));
-    pKid->m_RefObjNum = pRef ? pRef->GetRefObjNum() : 0;
-    pKid->m_PageObjNum = PageObjNum;
-    pKid->m_ContentId = pKidDict->GetIntegerFor("MCID");
+    kid.m_RefObjNum = pRef ? pRef->GetRefObjNum() : 0;
+    kid.m_PageObjNum = page_obj_num;
+    kid.m_ContentId = pKidDict->GetIntegerFor("MCID");
     return;
   }
 
   if (type == "OBJR") {
-    pKid->m_Type = Kid::kObject;
+    kid.m_Type = Kid::kObject;
     RetainPtr<const CPDF_Reference> pObj =
         ToReference(pKidDict->GetObjectFor("Obj"));
-    pKid->m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0;
-    pKid->m_PageObjNum = PageObjNum;
+    kid.m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0;
+    kid.m_PageObjNum = page_obj_num;
     return;
   }
 
-  pKid->m_Type = Kid::kElement;
-  pKid->m_pDict.Reset(pKidDict);
+  kid.m_Type = Kid::kElement;
+  kid.m_pDict.Reset(pKidDict);
 }
diff --git a/core/fpdfdoc/cpdf_structelement.h b/core/fpdfdoc/cpdf_structelement.h
index 6dd8f8f..72a0df3 100644
--- a/core/fpdfdoc/cpdf_structelement.h
+++ b/core/fpdfdoc/cpdf_structelement.h
@@ -62,10 +62,10 @@
                      RetainPtr<const CPDF_Dictionary> pDict);
   ~CPDF_StructElement() override;
 
-  void LoadKids(RetainPtr<const CPDF_Dictionary> pDict);
-  void LoadKid(uint32_t PageObjNum,
+  void LoadKids();
+  void LoadKid(uint32_t page_obj_num,
                RetainPtr<const CPDF_Object> pKidObj,
-               Kid* pKid);
+               Kid& kid);
 
   UnownedPtr<const CPDF_StructTree> const m_pTree;
   RetainPtr<const CPDF_Dictionary> const m_pDict;
diff --git a/core/fpdfdoc/cpdf_structtree.cpp b/core/fpdfdoc/cpdf_structtree.cpp
index 67ac948..2b00117 100644
--- a/core/fpdfdoc/cpdf_structtree.cpp
+++ b/core/fpdfdoc/cpdf_structtree.cpp
@@ -15,7 +15,6 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfdoc/cpdf_numbertree.h"
 #include "core/fpdfdoc/cpdf_structelement.h"
-#include "core/fxcrt/stl_util.h"
 
 namespace {
 
@@ -63,16 +62,16 @@
   if (!pKids)
     return;
 
-  uint32_t dwKids = 0;
+  size_t kids_count;
   if (pKids->IsDictionary())
-    dwKids = 1;
+    kids_count = 1;
   else if (const CPDF_Array* pArray = pKids->AsArray())
-    dwKids = fxcrt::CollectionSize<uint32_t>(*pArray);
+    kids_count = pArray->size();
   else
     return;
 
   m_Kids.clear();
-  m_Kids.resize(dwKids);
+  m_Kids.resize(kids_count);
 
   RetainPtr<const CPDF_Dictionary> pParentTree =
       m_pTreeRoot->GetDictFor("ParentTree");
@@ -124,11 +123,12 @@
   if (!pParentElement)
     return pElement;
 
-  if (!pParentElement->UpdateKidIfElement(pDict, pElement.Get()))
+  if (!pParentElement->UpdateKidIfElement(pDict, pElement.Get())) {
     map->erase(key);
+    return pElement;
+  }
 
   pElement->SetParent(pParentElement.Get());
-
   return pElement;
 }
 
diff --git a/core/fpdfdoc/cpvt_fontmap.cpp b/core/fpdfdoc/cpvt_fontmap.cpp
index d26655f..b492b5f 100644
--- a/core/fpdfdoc/cpvt_fontmap.cpp
+++ b/core/fpdfdoc/cpvt_fontmap.cpp
@@ -77,17 +77,14 @@
 int32_t CPVT_FontMap::GetWordFontIndex(uint16_t word,
                                        FX_Charset charset,
                                        int32_t nFontIndex) {
-  NOTREACHED();
-  return 0;
+  NOTREACHED_NORETURN();
 }
 
 int32_t CPVT_FontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) {
-  NOTREACHED();
-  return 0;
+  NOTREACHED_NORETURN();
 }
 
 FX_Charset CPVT_FontMap::CharSetFromUnicode(uint16_t word,
                                             FX_Charset nOldCharset) {
-  NOTREACHED();
-  return FX_Charset::kANSI;
+  NOTREACHED_NORETURN();
 }
diff --git a/core/fpdfdoc/cpvt_section.cpp b/core/fpdfdoc/cpvt_section.cpp
index 9276d0c..5c452da 100644
--- a/core/fpdfdoc/cpvt_section.cpp
+++ b/core/fpdfdoc/cpvt_section.cpp
@@ -12,7 +12,6 @@
 #include "core/fpdfdoc/cpvt_wordinfo.h"
 #include "core/fxcrt/stl_util.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -223,8 +222,8 @@
 
 CPVT_WordPlace CPVT_Section::AddWord(const CPVT_WordPlace& place,
                                      const CPVT_WordInfo& wordinfo) {
-  int32_t nWordIndex = pdfium::clamp(
-      place.nWordIndex, 0, fxcrt::CollectionSize<int32_t>(m_WordArray));
+  int32_t nWordIndex = std::clamp(place.nWordIndex, 0,
+                                  fxcrt::CollectionSize<int32_t>(m_WordArray));
   m_WordArray.insert(m_WordArray.begin() + nWordIndex,
                      std::make_unique<CPVT_WordInfo>(wordinfo));
   return place;
diff --git a/core/fpdfdoc/cpvt_variabletext.cpp b/core/fpdfdoc/cpvt_variabletext.cpp
index a13af1c..f9cab3e 100644
--- a/core/fpdfdoc/cpvt_variabletext.cpp
+++ b/core/fpdfdoc/cpvt_variabletext.cpp
@@ -18,7 +18,6 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/stl_util.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -528,7 +527,7 @@
   if (IsValid() && !m_bMultiLine)
     return place;
 
-  int32_t nSecIndex = pdfium::clamp(
+  int32_t nSecIndex = std::clamp(
       place.nSecIndex, 0, fxcrt::CollectionSize<int32_t>(m_SectionArray));
 
   auto pSection = std::make_unique<CPVT_Section>(this);
@@ -554,8 +553,8 @@
 
   CPVT_WordPlace newplace = place;
   newplace.nSecIndex =
-      pdfium::clamp(newplace.nSecIndex, 0,
-                    fxcrt::CollectionSize<int32_t>(m_SectionArray) - 1);
+      std::clamp(newplace.nSecIndex, 0,
+                 fxcrt::CollectionSize<int32_t>(m_SectionArray) - 1);
   return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo);
 }
 
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index 697e202..99f3365 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -30,7 +30,6 @@
 #include "core/fxcrt/stl_util.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -544,7 +543,7 @@
 
 CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation()
     const {
-  DCHECK_NE(m_pPage->GetPageObjectCount(), 0);
+  DCHECK_NE(m_pPage->GetPageObjectCount(), 0u);
 
   const int32_t nPageWidth = static_cast<int32_t>(m_pPage->GetPageWidth());
   const int32_t nPageHeight = static_cast<int32_t>(m_pPage->GetPageHeight());
@@ -563,13 +562,13 @@
       continue;
 
     int32_t minH = static_cast<int32_t>(
-        pdfium::clamp<float>(pPageObj->GetRect().left, 0.0f, nPageWidth));
+        std::clamp<float>(pPageObj->GetRect().left, 0.0f, nPageWidth));
     int32_t maxH = static_cast<int32_t>(
-        pdfium::clamp<float>(pPageObj->GetRect().right, 0.0f, nPageWidth));
+        std::clamp<float>(pPageObj->GetRect().right, 0.0f, nPageWidth));
     int32_t minV = static_cast<int32_t>(
-        pdfium::clamp<float>(pPageObj->GetRect().bottom, 0.0f, nPageHeight));
+        std::clamp<float>(pPageObj->GetRect().bottom, 0.0f, nPageHeight));
     int32_t maxV = static_cast<int32_t>(
-        pdfium::clamp<float>(pPageObj->GetRect().top, 0.0f, nPageHeight));
+        std::clamp<float>(pPageObj->GetRect().top, 0.0f, nPageHeight));
     if (minH >= maxH || minV >= maxV)
       continue;
 
diff --git a/core/fpdftext/cpdf_textpagefind.cpp b/core/fpdftext/cpdf_textpagefind.cpp
index 3f4ed4a..4191748 100644
--- a/core/fpdftext/cpdf_textpagefind.cpp
+++ b/core/fpdftext/cpdf_textpagefind.cpp
@@ -17,7 +17,7 @@
 #include "core/fxcrt/fx_unicode.h"
 #include "core/fxcrt/stl_util.h"
 #include "third_party/base/check.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
diff --git a/core/fxcodec/basic/basicmodule.h b/core/fxcodec/basic/basicmodule.h
index a257349..61e3735 100644
--- a/core/fxcodec/basic/basicmodule.h
+++ b/core/fxcodec/basic/basicmodule.h
@@ -12,7 +12,7 @@
 #include <memory>
 
 #include "core/fxcrt/data_vector.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcodec {
 
diff --git a/core/fxcodec/bmp/bmp_decoder.h b/core/fxcodec/bmp/bmp_decoder.h
index 46de34d..08a1638 100644
--- a/core/fxcodec/bmp/bmp_decoder.h
+++ b/core/fxcodec/bmp/bmp_decoder.h
@@ -13,7 +13,7 @@
 #include "core/fxcodec/progressive_decoder_iface.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #ifndef PDF_ENABLE_XFA_BMP
 #error "BMP must be enabled"
diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.h b/core/fxcodec/bmp/cfx_bmpdecompressor.h
index 408e39c..6432777 100644
--- a/core/fxcodec/bmp/cfx_bmpdecompressor.h
+++ b/core/fxcodec/bmp/cfx_bmpdecompressor.h
@@ -16,7 +16,7 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_CodecMemory;
 
diff --git a/core/fxcodec/cfx_codec_memory.cpp b/core/fxcodec/cfx_codec_memory.cpp
index e3e1ffa..8ac15d5 100644
--- a/core/fxcodec/cfx_codec_memory.cpp
+++ b/core/fxcodec/cfx_codec_memory.cpp
@@ -4,10 +4,10 @@
 
 #include "core/fxcodec/cfx_codec_memory.h"
 
-#include <string.h>
-
 #include <algorithm>
 
+#include "core/fxcrt/span_util.h"
+
 CFX_CodecMemory::CFX_CodecMemory(size_t buffer_size)
     : buffer_(FX_Alloc(uint8_t, buffer_size)), size_(buffer_size) {}
 
@@ -26,7 +26,7 @@
     return 0;
 
   size_t bytes_to_read = std::min(buffer.size(), size_ - pos_);
-  memcpy(buffer.data(), buffer_.get() + pos_, bytes_to_read);
+  fxcrt::spancpy(buffer, GetBufferSpan().subspan(pos_, bytes_to_read));
   pos_ += bytes_to_read;
   return bytes_to_read;
 }
@@ -44,6 +44,5 @@
 }
 
 void CFX_CodecMemory::Consume(size_t consumed) {
-  size_t unconsumed = size_ - consumed;
-  memmove(buffer_.get(), buffer_.get() + consumed, unconsumed);
+  fxcrt::spanmove(GetBufferSpan(), GetBufferSpan().subspan(consumed));
 }
diff --git a/core/fxcodec/cfx_codec_memory.h b/core/fxcodec/cfx_codec_memory.h
index a3044c3..2076311 100644
--- a/core/fxcodec/cfx_codec_memory.h
+++ b/core/fxcodec/cfx_codec_memory.h
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_CodecMemory final : public Retainable {
  public:
diff --git a/core/fxcodec/fax/faxmodule.cpp b/core/fxcodec/fax/faxmodule.cpp
index 075c72d..a904186 100644
--- a/core/fxcodec/fax/faxmodule.cpp
+++ b/core/fxcodec/fax/faxmodule.cpp
@@ -22,9 +22,8 @@
 #include "core/fxge/calculate_pitch.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "core/fxcrt/span_util.h"
@@ -127,7 +126,7 @@
 
 void FaxFillBits(uint8_t* dest_buf, int columns, int startpos, int endpos) {
   startpos = std::max(startpos, 0);
-  endpos = pdfium::clamp(endpos, 0, columns);
+  endpos = std::clamp(endpos, 0, columns);
   if (startpos >= endpos)
     return;
 
@@ -586,7 +585,7 @@
 
 void FaxDecoder::InvertBuffer() {
   DCHECK_EQ(m_Pitch, m_ScanlineBuf.size());
-  DCHECK_EQ(m_Pitch % 4, 0);
+  DCHECK_EQ(m_Pitch % 4, 0u);
   uint32_t* data = reinterpret_cast<uint32_t*>(m_ScanlineBuf.data());
   for (size_t i = 0; i < m_ScanlineBuf.size() / 4; ++i)
     data[i] = ~data[i];
diff --git a/core/fxcodec/fax/faxmodule.h b/core/fxcodec/fax/faxmodule.h
index 5f55cc5..2e7f5be 100644
--- a/core/fxcodec/fax/faxmodule.h
+++ b/core/fxcodec/fax/faxmodule.h
@@ -12,7 +12,7 @@
 #include <memory>
 
 #include "build/build_config.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "core/fxcrt/data_vector.h"
diff --git a/core/fxcodec/flate/flatemodule.cpp b/core/fxcodec/flate/flatemodule.cpp
index 8c397c5..f32955f 100644
--- a/core/fxcodec/flate/flatemodule.cpp
+++ b/core/fxcodec/flate/flatemodule.cpp
@@ -24,9 +24,9 @@
 #include "core/fxcrt/span_util.h"
 #include "core/fxge/calculate_pitch.h"
 #include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 #if defined(USE_SYSTEM_ZLIB)
 #include <zlib.h>
diff --git a/core/fxcodec/flate/flatemodule.h b/core/fxcodec/flate/flatemodule.h
index f82c542..f5a9509 100644
--- a/core/fxcodec/flate/flatemodule.h
+++ b/core/fxcodec/flate/flatemodule.h
@@ -13,7 +13,7 @@
 
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcodec {
 
diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp
index e7fc432..9811759 100644
--- a/core/fxcodec/gif/cfx_gifcontext.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext.cpp
@@ -7,6 +7,7 @@
 #include "core/fxcodec/gif/cfx_gifcontext.h"
 
 #include <stdint.h>
+#include <string.h>
 
 #include <algorithm>
 #include <iterator>
diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h
index 5e651f9..e4d89c0 100644
--- a/core/fxcodec/gif/cfx_gifcontext.h
+++ b/core/fxcodec/gif/cfx_gifcontext.h
@@ -15,7 +15,7 @@
 #include "core/fxcodec/gif/lzw_decompressor.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_CodecMemory;
 
diff --git a/core/fxcodec/gif/gif_decoder.h b/core/fxcodec/gif/gif_decoder.h
index cfcf0e9..1e2d39a 100644
--- a/core/fxcodec/gif/gif_decoder.h
+++ b/core/fxcodec/gif/gif_decoder.h
@@ -13,7 +13,7 @@
 #include "core/fxcodec/gif/cfx_gif.h"
 #include "core/fxcodec/progressive_decoder_iface.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #ifndef PDF_ENABLE_XFA_GIF
 #error "GIF must be enabled"
diff --git a/core/fxcodec/gif/lzw_decompressor.cpp b/core/fxcodec/gif/lzw_decompressor.cpp
index b13932b..dbfa0b9 100644
--- a/core/fxcodec/gif/lzw_decompressor.cpp
+++ b/core/fxcodec/gif/lzw_decompressor.cpp
@@ -13,8 +13,8 @@
 #include <utility>
 
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
 
 namespace fxcodec {
 
diff --git a/core/fxcodec/icc/icc_transform.cpp b/core/fxcodec/icc/icc_transform.cpp
index 18f8a41..485988d 100644
--- a/core/fxcodec/icc/icc_transform.cpp
+++ b/core/fxcodec/icc/icc_transform.cpp
@@ -12,10 +12,9 @@
 #include <memory>
 
 #include "core/fxcrt/data_vector.h"
-#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
 
 namespace fxcodec {
 
@@ -128,8 +127,7 @@
   } else {
     DataVector<uint8_t> inputs(std::max<size_t>(pSrcValues.size(), 16));
     for (size_t i = 0; i < pSrcValues.size(); ++i) {
-      inputs[i] =
-          pdfium::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
+      inputs[i] = std::clamp(static_cast<int>(pSrcValues[i] * 255.0f), 0, 255);
     }
     cmsDoTransform(m_hTransform, inputs.data(), output, 1);
   }
diff --git a/core/fxcodec/icc/icc_transform.h b/core/fxcodec/icc/icc_transform.h
index 78cf826..47cda07 100644
--- a/core/fxcodec/icc/icc_transform.h
+++ b/core/fxcodec/icc/icc_transform.h
@@ -12,7 +12,7 @@
 #include <memory>
 
 #include "core/fxcodec/fx_codec_def.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #if defined(USE_SYSTEM_LCMS2)
 #include <lcms2.h>
diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
index 2555ee2..625ebf2 100644
--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
+++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp
@@ -66,8 +66,7 @@
 CJBig2_ArithDecoder::~CJBig2_ArithDecoder() = default;
 
 int CJBig2_ArithDecoder::Decode(JBig2ArithCtx* pCX) {
-  DCHECK(pCX);
-  DCHECK_LT(pCX->I(), std::size(kQeTable));
+  CHECK_LT(pCX->I(), std::size(kQeTable));
 
   const JBig2ArithCtx::JBig2ArithQe& qe = kQeTable[pCX->I()];
   m_A -= qe.Qe;
diff --git a/core/fxcodec/jbig2/JBig2_BitStream.cpp b/core/fxcodec/jbig2/JBig2_BitStream.cpp
index 5f89716..6802c1b 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream.cpp
+++ b/core/fxcodec/jbig2/JBig2_BitStream.cpp
@@ -166,7 +166,7 @@
 }
 
 const uint8_t* CJBig2_BitStream::getPointer() const {
-  return getBuf() + m_dwByteIdx;
+  return m_Span.subspan(m_dwByteIdx).data();
 }
 
 void CJBig2_BitStream::offset(uint32_t dwOffset) {
diff --git a/core/fxcodec/jbig2/JBig2_BitStream.h b/core/fxcodec/jbig2/JBig2_BitStream.h
index b33f548..0a3a44d 100644
--- a/core/fxcodec/jbig2/JBig2_BitStream.h
+++ b/core/fxcodec/jbig2/JBig2_BitStream.h
@@ -8,7 +8,7 @@
 #define CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_
 
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CJBig2_BitStream {
  public:
diff --git a/core/fxcodec/jbig2/JBig2_Context.cpp b/core/fxcodec/jbig2/JBig2_Context.cpp
index 31b6f72..0d9028e 100644
--- a/core/fxcodec/jbig2/JBig2_Context.cpp
+++ b/core/fxcodec/jbig2/JBig2_Context.cpp
@@ -26,7 +26,7 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/pauseindicator_iface.h"
 #include "third_party/base/check.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 namespace {
 
diff --git a/core/fxcodec/jbig2/JBig2_Context.h b/core/fxcodec/jbig2/JBig2_Context.h
index 279db5b..5572eb7 100644
--- a/core/fxcodec/jbig2/JBig2_Context.h
+++ b/core/fxcodec/jbig2/JBig2_Context.h
@@ -17,7 +17,7 @@
 #include "core/fxcodec/jbig2/JBig2_Page.h"
 #include "core/fxcodec/jbig2/JBig2_Segment.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_GRDProc;
diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.h b/core/fxcodec/jbig2/JBig2_GrdProc.h
index 197eb91..c543ea1 100644
--- a/core/fxcodec/jbig2/JBig2_GrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_GrdProc.h
@@ -14,6 +14,7 @@
 #include "core/fxcodec/fx_codec_def.h"
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
@@ -28,7 +29,7 @@
     ProgressiveArithDecodeState();
     ~ProgressiveArithDecodeState();
 
-    std::unique_ptr<CJBig2_Image>* pImage;
+    UnownedPtr<std::unique_ptr<CJBig2_Image>> pImage;
     UnownedPtr<CJBig2_ArithDecoder> pArithDecoder;
     UnownedPtr<JBig2ArithCtx> gbContext;
     UnownedPtr<PauseIndicatorIface> pPause;
@@ -94,7 +95,7 @@
       JBig2ArithCtx* gbContext);
 
   uint32_t m_loopIndex = 0;
-  uint8_t* m_pLine = nullptr;
+  UNOWNED_PTR_EXCLUSION uint8_t* m_pLine = nullptr;
   FXCODEC_STATUS m_ProgressiveStatus;
   uint16_t m_DecodeType = 0;
   int m_LTP = 0;
diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
index 1c0f882..16a87cc 100644
--- a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
+++ b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp
@@ -13,6 +13,10 @@
 #include "core/fxcodec/jbig2/JBig2_GrdProc.h"
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 
+CJBig2_HTRDProc::CJBig2_HTRDProc() = default;
+
+CJBig2_HTRDProc::~CJBig2_HTRDProc() = default;
+
 std::unique_ptr<CJBig2_Image> CJBig2_HTRDProc::DecodeArith(
     CJBig2_ArithDecoder* pArithDecoder,
     JBig2ArithCtx* gbContext,
diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.h b/core/fxcodec/jbig2/JBig2_HtrdProc.h
index 30e9d9c..2073d8b 100644
--- a/core/fxcodec/jbig2/JBig2_HtrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_HtrdProc.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_BitStream;
@@ -21,6 +22,9 @@
 
 class CJBig2_HTRDProc {
  public:
+  CJBig2_HTRDProc();
+  ~CJBig2_HTRDProc();
+
   std::unique_ptr<CJBig2_Image> DecodeArith(CJBig2_ArithDecoder* pArithDecoder,
                                             JBig2ArithCtx* gbContext,
                                             PauseIndicatorIface* pPause);
@@ -33,7 +37,7 @@
   bool HMMR;
   uint8_t HTEMPLATE;
   uint32_t HNUMPATS;
-  const std::vector<std::unique_ptr<CJBig2_Image>>* HPATS;
+  UnownedPtr<const std::vector<std::unique_ptr<CJBig2_Image>>> HPATS;
   bool HDEFPIXEL;
   JBig2ComposeOp HCOMBOP;
   bool HENABLESKIP;
diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
index fcbdb34..f59ab4d 100644
--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
+++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp
@@ -12,6 +12,7 @@
 #include "core/fxcodec/jbig2/JBig2_BitStream.h"
 #include "core/fxcodec/jbig2/JBig2_Context.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "third_party/base/check.h"
 
 namespace {
@@ -24,7 +25,7 @@
 
 struct HuffmanTable {
   bool HTOOB;
-  const JBig2TableLine* lines;
+  UNOWNED_PTR_EXCLUSION const JBig2TableLine* lines;
   size_t size;
 };
 
diff --git a/core/fxcodec/jbig2/JBig2_Image.h b/core/fxcodec/jbig2/JBig2_Image.h
index 4f751f4..1fe8402 100644
--- a/core/fxcodec/jbig2/JBig2_Image.h
+++ b/core/fxcodec/jbig2/JBig2_Image.h
@@ -12,7 +12,7 @@
 #include "core/fxcodec/jbig2/JBig2_Define.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/maybe_owned.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 struct FX_RECT;
 
diff --git a/core/fxcodec/jbig2/JBig2_SddProc.h b/core/fxcodec/jbig2/JBig2_SddProc.h
index 09478eb..a1e7590 100644
--- a/core/fxcodec/jbig2/JBig2_SddProc.h
+++ b/core/fxcodec/jbig2/JBig2_SddProc.h
@@ -14,7 +14,8 @@
 
 #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/containers/span.h"
 
 class CJBig2_BitStream;
 class CJBig2_HuffmanTable;
@@ -43,7 +44,7 @@
   uint32_t SDNUMINSYMS;
   uint32_t SDNUMNEWSYMS;
   uint32_t SDNUMEXSYMS;
-  CJBig2_Image** SDINSYMS;
+  UNOWNED_PTR_EXCLUSION CJBig2_Image** SDINSYMS;
   UnownedPtr<const CJBig2_HuffmanTable> SDHUFFDH;
   UnownedPtr<const CJBig2_HuffmanTable> SDHUFFDW;
   UnownedPtr<const CJBig2_HuffmanTable> SDHUFFBMSIZE;
diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.h b/core/fxcodec/jbig2/JBig2_TrdProc.h
index 7962c70..bd887d9 100644
--- a/core/fxcodec/jbig2/JBig2_TrdProc.h
+++ b/core/fxcodec/jbig2/JBig2_TrdProc.h
@@ -14,6 +14,7 @@
 
 #include "core/fxcodec/jbig2/JBig2_Image.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
 class CJBig2_ArithDecoder;
 class CJBig2_ArithIaidDecoder;
@@ -71,7 +72,7 @@
   uint32_t SBSTRIPS;
   uint32_t SBNUMSYMS;
   std::vector<JBig2HuffmanCode> SBSYMCODES;
-  CJBig2_Image** SBSYMS;
+  UNOWNED_PTR_EXCLUSION CJBig2_Image** SBSYMS;
   JBig2ComposeOp SBCOMBOP;
   JBig2Corner REFCORNER;
   UnownedPtr<const CJBig2_HuffmanTable> SBHUFFFS;
diff --git a/core/fxcodec/jbig2/jbig2_decoder.h b/core/fxcodec/jbig2/jbig2_decoder.h
index 0a94f5d..745fa2e 100644
--- a/core/fxcodec/jbig2/jbig2_decoder.h
+++ b/core/fxcodec/jbig2/jbig2_decoder.h
@@ -10,7 +10,8 @@
 #include <memory>
 
 #include "core/fxcodec/fx_codec_def.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/containers/span.h"
 
 class CJBig2_Context;
 class JBig2_DocumentContext;
@@ -29,7 +30,7 @@
   uint64_t m_nSrcKey = 0;
   pdfium::span<const uint8_t> m_pGlobalSpan;
   pdfium::span<const uint8_t> m_pSrcSpan;
-  uint8_t* m_dest_buf = nullptr;
+  UNOWNED_PTR_EXCLUSION uint8_t* m_dest_buf = nullptr;
   uint32_t m_dest_pitch = 0;
   std::unique_ptr<CJBig2_Context> m_pContext;
 };
diff --git a/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp b/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp
index c662f14..1f1a7c3 100644
--- a/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp
+++ b/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp
@@ -17,7 +17,7 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/check.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 class CJpegContext final : public ProgressiveDecoderIface::Context {
  public:
diff --git a/core/fxcodec/jpeg/jpegmodule.h b/core/fxcodec/jpeg/jpegmodule.h
index a4e20ef..0f61765 100644
--- a/core/fxcodec/jpeg/jpegmodule.h
+++ b/core/fxcodec/jpeg/jpegmodule.h
@@ -13,7 +13,7 @@
 
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "core/fxcrt/retain_ptr.h"
diff --git a/core/fxcodec/jpx/cjpx_decoder.cpp b/core/fxcodec/jpx/cjpx_decoder.cpp
index 9391d61..2e7a72a 100644
--- a/core/fxcodec/jpx/cjpx_decoder.cpp
+++ b/core/fxcodec/jpx/cjpx_decoder.cpp
@@ -18,8 +18,7 @@
 #include "core/fxcrt/span_util.h"
 #include "core/fxge/calculate_pitch.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/cxx17_backports.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 #if !defined(USE_SYSTEM_LIBOPENJPEG2)
 #include "third_party/libopenjpeg/opj_malloc.h"
@@ -88,9 +87,9 @@
                  int* out_b) {
   cb -= offset;
   cr -= offset;
-  *out_r = pdfium::clamp(y + static_cast<int>(1.402 * cr), 0, upb);
-  *out_g = pdfium::clamp(y - static_cast<int>(0.344 * cb + 0.714 * cr), 0, upb);
-  *out_b = pdfium::clamp(y + static_cast<int>(1.772 * cb), 0, upb);
+  *out_r = std::clamp(y + static_cast<int>(1.402 * cr), 0, upb);
+  *out_g = std::clamp(y - static_cast<int>(0.344 * cb + 0.714 * cr), 0, upb);
+  *out_b = std::clamp(y + static_cast<int>(1.772 * cb), 0, upb);
 }
 
 void sycc444_to_rgb(opj_image_t* img) {
@@ -594,7 +593,7 @@
           uint8_t* pPixel = pScanline + col * channel_count;
           int src = comps.data[row * width + col] + src_offset;
           int pixel = (src >> adjust) + ((src >> (adjust - 1)) % 2);
-          pixel = pdfium::clamp(pixel, 0, 255);
+          pixel = std::clamp(pixel, 0, 255);
           *pPixel = static_cast<uint8_t>(pixel);
         }
       }
diff --git a/core/fxcodec/jpx/cjpx_decoder.h b/core/fxcodec/jpx/cjpx_decoder.h
index 36116b3..c6e67c8 100644
--- a/core/fxcodec/jpx/cjpx_decoder.h
+++ b/core/fxcodec/jpx/cjpx_decoder.h
@@ -12,7 +12,7 @@
 #include <memory>
 
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #if defined(USE_SYSTEM_LIBOPENJPEG2)
 #include <openjpeg.h>
diff --git a/core/fxcodec/jpx/jpx_unittest.cpp b/core/fxcodec/jpx/jpx_unittest.cpp
index b89a910..84e6a35 100644
--- a/core/fxcodec/jpx/jpx_unittest.cpp
+++ b/core/fxcodec/jpx/jpx_unittest.cpp
@@ -9,6 +9,7 @@
 
 #include "core/fxcodec/jpx/cjpx_decoder.h"
 #include "core/fxcodec/jpx/jpx_decode_utils.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libopenjpeg/opj_malloc.h"
@@ -424,9 +425,9 @@
         opj_image_data_alloc(v.w * v.h * sizeof(OPJ_INT32)));
     u.data = static_cast<OPJ_INT32*>(
         opj_image_data_alloc(u.w * u.h * sizeof(OPJ_INT32)));
-    memset(y.data, 1, y.w * y.h * sizeof(OPJ_INT32));
-    memset(u.data, 0, u.w * u.h * sizeof(OPJ_INT32));
-    memset(v.data, 0, v.w * v.h * sizeof(OPJ_INT32));
+    FXSYS_memset(y.data, 1, y.w * y.h * sizeof(OPJ_INT32));
+    FXSYS_memset(u.data, 0, u.w * u.h * sizeof(OPJ_INT32));
+    FXSYS_memset(v.data, 0, v.w * v.h * sizeof(OPJ_INT32));
     img.comps[0] = y;
     img.comps[1] = u;
     img.comps[2] = v;
diff --git a/core/fxcodec/progressive_decoder.cpp b/core/fxcodec/progressive_decoder.cpp
index a65e2bb..8e84500 100644
--- a/core/fxcodec/progressive_decoder.cpp
+++ b/core/fxcodec/progressive_decoder.cpp
@@ -633,8 +633,6 @@
           *scan_des++ = CStretchEngine::PixelFromFixed(dest_a);
         }
         break;
-      default:
-        return;
     }
   }
 }
@@ -908,8 +906,6 @@
           *scan_des++ = CStretchEngine::PixelFromFixed(dest_a);
         }
         break;
-      default:
-        return;
     }
   }
   int dest_bottom = dest_top + m_sizeY - 1;
@@ -1999,8 +1995,6 @@
           *scan_des++ = CStretchEngine::PixelFromFixed(dest_a);
         }
         break;
-      default:
-        return;
     }
   }
   int dest_bottom = dest_top + m_sizeY;
diff --git a/core/fxcodec/progressive_decoder.h b/core/fxcodec/progressive_decoder.h
index 2f8d2ab..6325c88 100644
--- a/core/fxcodec/progressive_decoder.h
+++ b/core/fxcodec/progressive_decoder.h
@@ -18,9 +18,10 @@
 #include "core/fxcodec/progressive_decoder_iface.h"
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxge/dib/cstretchengine.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #ifdef PDF_ENABLE_XFA_BMP
 #include "core/fxcodec/bmp/bmp_decoder.h"
@@ -263,7 +264,7 @@
   size_t m_FrameCur = 0;
 #ifdef PDF_ENABLE_XFA_GIF
   int m_GifBgIndex = 0;
-  CFX_GifPalette* m_pGifPalette = nullptr;
+  UNOWNED_PTR_EXCLUSION CFX_GifPalette* m_pGifPalette = nullptr;
   int32_t m_GifPltNumber = 0;
   int m_GifTransIndex = -1;
   FX_RECT m_GifFrameRect;
diff --git a/core/fxcodec/progressive_decoder_unittest.cpp b/core/fxcodec/progressive_decoder_unittest.cpp
index cdc0ae1..ccb0c03 100644
--- a/core/fxcodec/progressive_decoder_unittest.cpp
+++ b/core/fxcodec/progressive_decoder_unittest.cpp
@@ -22,7 +22,7 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #ifdef PDF_ENABLE_XFA_BMP
 #include "core/fxcodec/bmp/bmp_decoder.h"
diff --git a/core/fxcodec/scanlinedecoder.h b/core/fxcodec/scanlinedecoder.h
index 0a1db37..f319147 100644
--- a/core/fxcodec/scanlinedecoder.h
+++ b/core/fxcodec/scanlinedecoder.h
@@ -9,7 +9,7 @@
 
 #include <stdint.h>
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class PauseIndicatorIface;
 
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 09400e8..c9bdfa0 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build_overrides/build.gni")
 import("../../pdfium.gni")
 import("../../testing/test.gni")
 
@@ -9,6 +10,12 @@
   sources = [ "unowned_ptr.h" ]
   deps = [ "../../third_party:pdfium_compiler_specific" ]
   configs += [ "../../:pdfium_strict_config" ]
+  if (pdf_use_partition_alloc) {
+    public_deps = [
+      "//base/allocator/partition_allocator:partition_alloc_buildflags",
+      "//base/allocator/partition_allocator:raw_ptr",
+    ]
+  }
 }
 
 source_set("fxcrt") {
@@ -34,10 +41,7 @@
     "cfx_seekablestreamproxy.h",
     "cfx_timer.cpp",
     "cfx_timer.h",
-    "cfx_utf8decoder.cpp",
-    "cfx_utf8decoder.h",
-    "cfx_utf8encoder.cpp",
-    "cfx_utf8encoder.h",
+    "code_point_view.h",
     "data_vector.h",
     "fileaccess_iface.h",
     "fixed_size_data_vector.h",
@@ -55,6 +59,7 @@
     "fx_extension.cpp",
     "fx_extension.h",
     "fx_folder.h",
+    "fx_memcpy_wrappers.h",
     "fx_memory.cpp",
     "fx_memory.h",
     "fx_memory_wrappers.h",
@@ -89,6 +94,7 @@
     "string_pool_template.h",
     "string_view_template.h",
     "tree_node.h",
+    "utf16.h",
     "weak_ptr.h",
     "widestring.cpp",
     "widestring.h",
@@ -136,7 +142,11 @@
   ]
   if (pdf_use_partition_alloc) {
     sources += [ "fx_memory_pa.cpp" ]
-    deps += [ "//base/allocator/partition_allocator:partition_alloc" ]
+    deps += [
+      "//base/allocator/partition_allocator:partition_alloc",
+      "//base/allocator/partition_allocator:partition_alloc_buildflags",
+      "//base/allocator/partition_allocator:raw_ptr",
+    ]
   } else {
     sources += [ "fx_memory_malloc.cpp" ]
   }
@@ -162,6 +172,13 @@
   }
 }
 
+source_set("test_support") {
+  testonly = true
+  sources = [ "string_test_support.cpp" ]
+  configs += [ "../../:pdfium_strict_config" ]
+  deps = [ ":fxcrt" ]
+}
+
 source_set("unit_test_support") {
   testonly = true
   sources = [
@@ -171,6 +188,7 @@
   configs += [ "../../:pdfium_strict_config" ]
   deps = [
     ":fxcrt",
+    ":test_support",
     "//testing/gtest",
   ]
 }
@@ -186,12 +204,14 @@
     "cfx_datetime_unittest.cpp",
     "cfx_seekablestreamproxy_unittest.cpp",
     "cfx_timer_unittest.cpp",
+    "code_point_view_unittest.cpp",
     "fixed_try_alloc_zeroed_data_vector_unittest.cpp",
     "fixed_uninit_data_vector_unittest.cpp",
     "fixed_zeroed_data_vector_unittest.cpp",
     "fx_bidi_unittest.cpp",
     "fx_coordinates_unittest.cpp",
     "fx_extension_unittest.cpp",
+    "fx_memcpy_wrappers_unittest.cpp",
     "fx_memory_unittest.cpp",
     "fx_memory_wrappers_unittest.cpp",
     "fx_number_unittest.cpp",
@@ -212,6 +232,7 @@
     "string_pool_template_unittest.cpp",
     "tree_node_unittest.cpp",
     "unowned_ptr_unittest.cpp",
+    "utf16_unittest.cpp",
     "weak_ptr_unittest.cpp",
     "widestring_unittest.cpp",
     "widetext_buffer_unittest.cpp",
@@ -225,7 +246,9 @@
   ]
   deps = [ ":unit_test_support" ]
   pdfium_root_dir = "../../"
-
+  if (pdf_use_partition_alloc) {
+    deps += [ "//base/allocator/partition_allocator:partition_alloc" ]
+  }
   if (pdf_enable_xfa) {
     sources += [ "cfx_memorystream_unittest.cpp" ]
     deps += [ "../fpdfapi/parser" ]
diff --git a/core/fxcrt/autonuller.h b/core/fxcrt/autonuller.h
index 12df63f..88ae0cb 100644
--- a/core/fxcrt/autonuller.h
+++ b/core/fxcrt/autonuller.h
@@ -6,6 +6,7 @@
 #define CORE_FXCRT_AUTONULLER_H_
 
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 namespace fxcrt {
 
@@ -22,7 +23,7 @@
   void AbandonNullification() { m_Location = nullptr; }
 
  private:
-  T* m_Location;
+  UnownedPtr<T> m_Location;
 };
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/autorestorer.h b/core/fxcrt/autorestorer.h
index 02b5f3d..cecd0cd 100644
--- a/core/fxcrt/autorestorer.h
+++ b/core/fxcrt/autorestorer.h
@@ -6,6 +6,7 @@
 #define CORE_FXCRT_AUTORESTORER_H_
 
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
 
 namespace fxcrt {
 
@@ -23,7 +24,7 @@
   void AbandonRestoration() { m_Location = nullptr; }
 
  private:
-  T* m_Location;
+  UnownedPtr<T> m_Location;
   const T m_OldValue;
 };
 
diff --git a/core/fxcrt/binary_buffer.h b/core/fxcrt/binary_buffer.h
index 9150266..26f0caf 100644
--- a/core/fxcrt/binary_buffer.h
+++ b/core/fxcrt/binary_buffer.h
@@ -12,7 +12,7 @@
 
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/data_vector.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp
index d3e1cc0..d2ba60e 100644
--- a/core/fxcrt/bytestring.cpp
+++ b/core/fxcrt/bytestring.cpp
@@ -14,17 +14,16 @@
 #include <string>
 #include <utility>
 
-#include "core/fxcrt/cfx_utf8decoder.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/span.h"
 
 template class fxcrt::StringDataTemplate<char>;
 template class fxcrt::StringViewTemplate<char>;
@@ -263,7 +262,7 @@
     return m_pData->m_nDataLength == 0;
 
   return strlen(ptr) == m_pData->m_nDataLength &&
-         memcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
+         FXSYS_memcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
 }
 
 bool ByteString::operator==(ByteStringView str) const {
@@ -271,8 +270,8 @@
     return str.IsEmpty();
 
   return m_pData->m_nDataLength == str.GetLength() &&
-         memcmp(m_pData->m_String, str.unterminated_c_str(), str.GetLength()) ==
-             0;
+         FXSYS_memcmp(m_pData->m_String, str.unterminated_c_str(),
+                      str.GetLength()) == 0;
 }
 
 bool ByteString::operator==(const ByteString& other) const {
@@ -298,7 +297,7 @@
 
   size_t len = GetLength();
   size_t other_len = ptr ? strlen(ptr) : 0;
-  int result = memcmp(c_str(), ptr, std::min(len, other_len));
+  int result = FXSYS_memcmp(c_str(), ptr, std::min(len, other_len));
   return result < 0 || (result == 0 && len < other_len);
 }
 
@@ -312,7 +311,7 @@
 
   size_t len = GetLength();
   size_t other_len = other.GetLength();
-  int result = memcmp(c_str(), other.c_str(), std::min(len, other_len));
+  int result = FXSYS_memcmp(c_str(), other.c_str(), std::min(len, other_len));
   return result < 0 || (result == 0 && len < other_len);
 }
 
@@ -433,8 +432,9 @@
     return 0;
 
   size_t old_length = m_pData->m_nDataLength;
-  if (count == 0 || index != pdfium::clamp<size_t>(index, 0, old_length))
+  if (count == 0 || index != std::clamp<size_t>(index, 0, old_length)) {
     return old_length;
+  }
 
   size_t removal_length = index + count;
   if (removal_length > old_length)
@@ -442,8 +442,8 @@
 
   ReallocBeforeWrite(old_length);
   size_t chars_to_copy = old_length - removal_length + 1;
-  memmove(m_pData->m_String + index, m_pData->m_String + removal_length,
-          chars_to_copy);
+  FXSYS_memmove(m_pData->m_String + index, m_pData->m_String + removal_length,
+                chars_to_copy);
   m_pData->m_nDataLength = old_length - count;
   return m_pData->m_nDataLength;
 }
@@ -535,8 +535,8 @@
 
   const size_t new_length = cur_length + 1;
   ReallocBeforeWrite(new_length);
-  memmove(m_pData->m_String + index + 1, m_pData->m_String + index,
-          new_length - index);
+  FXSYS_memmove(m_pData->m_String + index + 1, m_pData->m_String + index,
+                new_length - index);
   m_pData->m_String[index] = ch;
   m_pData->m_nDataLength = new_length;
   return new_length;
@@ -549,8 +549,8 @@
   if (!IsValidIndex(start))
     return absl::nullopt;
 
-  const char* pStr = static_cast<const char*>(
-      memchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start));
+  const char* pStr = static_cast<const char*>(FXSYS_memchr(
+      m_pData->m_String + start, ch, m_pData->m_nDataLength - start));
   return pStr ? absl::optional<size_t>(
                     static_cast<size_t>(pStr - m_pData->m_String))
               : absl::nullopt;
@@ -669,13 +669,13 @@
   for (size_t i = 0; i < nCount; i++) {
     const char* pTarget = FX_strstr(pStart, static_cast<int>(pEnd - pStart),
                                     pOld.unterminated_c_str(), nSourceLen);
-    memcpy(pDest, pStart, pTarget - pStart);
+    FXSYS_memcpy(pDest, pStart, pTarget - pStart);
     pDest += pTarget - pStart;
-    memcpy(pDest, pNew.unterminated_c_str(), pNew.GetLength());
+    FXSYS_memcpy(pDest, pNew.unterminated_c_str(), pNew.GetLength());
     pDest += pNew.GetLength();
     pStart = pTarget + nSourceLen;
   }
-  memcpy(pDest, pStart, pEnd - pStart);
+  FXSYS_memcpy(pDest, pStart, pEnd - pStart);
   m_pData.Swap(pNewData);
   return nCount;
 }
@@ -687,7 +687,8 @@
   size_t this_len = m_pData->m_nDataLength;
   size_t that_len = str.GetLength();
   size_t min_len = std::min(this_len, that_len);
-  int result = memcmp(m_pData->m_String, str.unterminated_c_str(), min_len);
+  int result =
+      FXSYS_memcmp(m_pData->m_String, str.unterminated_c_str(), min_len);
   if (result != 0)
     return result;
   if (this_len == that_len)
@@ -739,8 +740,8 @@
   if (pos) {
     ReallocBeforeWrite(len);
     size_t nDataLength = len - pos;
-    memmove(m_pData->m_String, m_pData->m_String + pos,
-            (nDataLength + 1) * sizeof(char));
+    FXSYS_memmove(m_pData->m_String, m_pData->m_String + pos,
+                  (nDataLength + 1) * sizeof(char));
     m_pData->m_nDataLength = nDataLength;
   }
 }
diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h
index 5f1ed53..3c23d62 100644
--- a/core/fxcrt/bytestring.h
+++ b/core/fxcrt/bytestring.h
@@ -23,7 +23,7 @@
 #include "core/fxcrt/string_view_template.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -286,6 +286,13 @@
 std::ostream& operator<<(std::ostream& os, const ByteString& str);
 std::ostream& operator<<(std::ostream& os, ByteStringView str);
 
+// This is declared here for use in gtest-based tests but is defined in a test
+// support target. This should not be used in production code. Just use
+// operator<< from above instead.
+// In some cases, gtest will automatically use operator<< as well, but in this
+// case, it needs PrintTo() because ByteString looks like a container to gtest.
+void PrintTo(const ByteString& str, std::ostream* os);
+
 }  // namespace fxcrt
 
 using ByteString = fxcrt::ByteString;
diff --git a/core/fxcrt/bytestring_unittest.cpp b/core/fxcrt/bytestring_unittest.cpp
index f00bb46..9d2f132 100644
--- a/core/fxcrt/bytestring_unittest.cpp
+++ b/core/fxcrt/bytestring_unittest.cpp
@@ -15,7 +15,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
diff --git a/core/fxcrt/cfx_bitstream.h b/core/fxcrt/cfx_bitstream.h
index c60cf0a..8e168b8 100644
--- a/core/fxcrt/cfx_bitstream.h
+++ b/core/fxcrt/cfx_bitstream.h
@@ -10,7 +10,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_BitStream {
  public:
diff --git a/core/fxcrt/cfx_datetime.cpp b/core/fxcrt/cfx_datetime.cpp
index 50f1c6a..526bba4 100644
--- a/core/fxcrt/cfx_datetime.cpp
+++ b/core/fxcrt/cfx_datetime.cpp
@@ -10,7 +10,7 @@
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_system.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
diff --git a/core/fxcrt/cfx_fileaccess_windows.h b/core/fxcrt/cfx_fileaccess_windows.h
index 2928d18..89c79e9 100644
--- a/core/fxcrt/cfx_fileaccess_windows.h
+++ b/core/fxcrt/cfx_fileaccess_windows.h
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "core/fxcrt/fileaccess_iface.h"
 #include "core/fxcrt/fx_types.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
 #if !BUILDFLAG(IS_WIN)
 #error "Included on the wrong platform"
@@ -39,7 +40,7 @@
   bool Truncate(FX_FILESIZE szFile) override;
 
  private:
-  void* m_hFile = nullptr;
+  UNOWNED_PTR_EXCLUSION void* m_hFile = nullptr;  // void type incompatible.
 };
 
 #endif  // CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_
diff --git a/core/fxcrt/cfx_memorystream.h b/core/fxcrt/cfx_memorystream.h
index 5ecf995..b2620fc 100644
--- a/core/fxcrt/cfx_memorystream.h
+++ b/core/fxcrt/cfx_memorystream.h
@@ -10,7 +10,7 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_MemoryStream final : public IFX_SeekableStream {
  public:
diff --git a/core/fxcrt/cfx_read_only_span_stream.h b/core/fxcrt/cfx_read_only_span_stream.h
index b1b30cd..d476640 100644
--- a/core/fxcrt/cfx_read_only_span_stream.h
+++ b/core/fxcrt/cfx_read_only_span_stream.h
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_ReadOnlySpanStream final : public IFX_SeekableReadStream {
  public:
diff --git a/core/fxcrt/cfx_read_only_string_stream.cpp b/core/fxcrt/cfx_read_only_string_stream.cpp
index 62d5505..364330b 100644
--- a/core/fxcrt/cfx_read_only_string_stream.cpp
+++ b/core/fxcrt/cfx_read_only_string_stream.cpp
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "core/fxcrt/cfx_read_only_span_stream.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 CFX_ReadOnlyStringStream::CFX_ReadOnlyStringStream(ByteString data)
     : data_(std::move(data)),
diff --git a/core/fxcrt/cfx_read_only_vector_stream.cpp b/core/fxcrt/cfx_read_only_vector_stream.cpp
index d6a5bf0..97f0dcb 100644
--- a/core/fxcrt/cfx_read_only_vector_stream.cpp
+++ b/core/fxcrt/cfx_read_only_vector_stream.cpp
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "core/fxcrt/cfx_read_only_span_stream.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 CFX_ReadOnlyVectorStream::CFX_ReadOnlyVectorStream(DataVector<uint8_t> data)
     : data_(std::move(data)),
diff --git a/core/fxcrt/cfx_seekablestreamproxy.cpp b/core/fxcrt/cfx_seekablestreamproxy.cpp
index dc311ae..6fb7eac 100644
--- a/core/fxcrt/cfx_seekablestreamproxy.cpp
+++ b/core/fxcrt/cfx_seekablestreamproxy.cpp
@@ -18,7 +18,6 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -73,7 +72,7 @@
 
 void UTF16ToWChar(void* pBuffer, size_t iLength) {
   DCHECK(pBuffer);
-  DCHECK_GT(iLength, 0);
+  DCHECK_GT(iLength, 0u);
 
   uint16_t* pSrc = static_cast<uint16_t*>(pBuffer);
   wchar_t* pDst = static_cast<wchar_t*>(pBuffer);
@@ -156,8 +155,7 @@
           new_pos.ValueOrDefault(std::numeric_limits<FX_FILESIZE>::max());
     } break;
   }
-  m_iPosition =
-      pdfium::clamp(m_iPosition, static_cast<FX_FILESIZE>(0), GetSize());
+  m_iPosition = std::clamp(m_iPosition, static_cast<FX_FILESIZE>(0), GetSize());
 }
 
 void CFX_SeekableStreamProxy::SetCodePage(FX_CodePage wCodePage) {
diff --git a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
index 94ebf66..05cc492 100644
--- a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
+++ b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp
@@ -9,7 +9,7 @@
 #include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(SeekableStreamProxyTest, NullStream) {
   auto proxy_stream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(
diff --git a/core/fxcrt/cfx_utf8decoder.cpp b/core/fxcrt/cfx_utf8decoder.cpp
deleted file mode 100644
index e834815..0000000
--- a/core/fxcrt/cfx_utf8decoder.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2017 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_utf8decoder.h"
-
-#include <utility>
-
-CFX_UTF8Decoder::CFX_UTF8Decoder(ByteStringView input) {
-  for (char c : input) {
-    ProcessByte(c);
-  }
-}
-
-CFX_UTF8Decoder::~CFX_UTF8Decoder() = default;
-
-WideString CFX_UTF8Decoder::TakeResult() {
-  return std::move(m_Buffer);
-}
-
-void CFX_UTF8Decoder::AppendCodePoint(uint32_t ch) {
-  m_Buffer += static_cast<wchar_t>(ch);
-}
-
-void CFX_UTF8Decoder::ProcessByte(uint8_t byte) {
-  if (byte < 0x80) {
-    m_PendingBytes = 0;
-    AppendCodePoint(byte);
-  } else if (byte < 0xc0) {
-    if (m_PendingBytes == 0) {
-      return;
-    }
-    m_PendingBytes--;
-    m_PendingChar |= (byte & 0x3f) << (m_PendingBytes * 6);
-    if (m_PendingBytes == 0) {
-      AppendCodePoint(m_PendingChar);
-    }
-  } else if (byte < 0xe0) {
-    m_PendingBytes = 1;
-    m_PendingChar = (byte & 0x1f) << 6;
-  } else if (byte < 0xf0) {
-    m_PendingBytes = 2;
-    m_PendingChar = (byte & 0x0f) << 12;
-  } else if (byte < 0xf8) {
-    m_PendingBytes = 3;
-    m_PendingChar = (byte & 0x07) << 18;
-  } else if (byte < 0xfc) {
-    m_PendingBytes = 4;
-    m_PendingChar = (byte & 0x03) << 24;
-  } else if (byte < 0xfe) {
-    m_PendingBytes = 5;
-    m_PendingChar = (byte & 0x01) << 30;
-  } else {
-    m_PendingBytes = 0;
-  }
-}
diff --git a/core/fxcrt/cfx_utf8decoder.h b/core/fxcrt/cfx_utf8decoder.h
deleted file mode 100644
index 35b5671..0000000
--- a/core/fxcrt/cfx_utf8decoder.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_UTF8DECODER_H_
-#define CORE_FXCRT_CFX_UTF8DECODER_H_
-
-#include "core/fxcrt/string_view_template.h"
-#include "core/fxcrt/widestring.h"
-
-class CFX_UTF8Decoder {
- public:
-  explicit CFX_UTF8Decoder(ByteStringView input);
-  ~CFX_UTF8Decoder();
-
-  WideString TakeResult();
-
- private:
-  void ProcessByte(uint8_t byte);
-  void AppendCodePoint(uint32_t ch);
-
-  int m_PendingBytes = 0;
-  uint32_t m_PendingChar = 0;
-  WideString m_Buffer;
-};
-
-#endif  // CORE_FXCRT_CFX_UTF8DECODER_H_
diff --git a/core/fxcrt/cfx_utf8encoder.cpp b/core/fxcrt/cfx_utf8encoder.cpp
deleted file mode 100644
index 4951126..0000000
--- a/core/fxcrt/cfx_utf8encoder.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2018 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxcrt/cfx_utf8encoder.h"
-
-CFX_UTF8Encoder::CFX_UTF8Encoder() = default;
-
-CFX_UTF8Encoder::~CFX_UTF8Encoder() = default;
-
-void CFX_UTF8Encoder::Input(wchar_t unicodeAsWchar) {
-  uint32_t unicode = static_cast<uint32_t>(unicodeAsWchar);
-  if (unicode < 0x80) {
-    m_Buffer.push_back(unicode);
-  } else {
-    if (unicode >= 0x80000000)
-      return;
-
-    int nbytes = 0;
-    if (unicode < 0x800)
-      nbytes = 2;
-    else if (unicode < 0x10000)
-      nbytes = 3;
-    else if (unicode < 0x200000)
-      nbytes = 4;
-    else if (unicode < 0x4000000)
-      nbytes = 5;
-    else
-      nbytes = 6;
-
-    static const uint8_t prefix[] = {0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
-    int order = 1 << ((nbytes - 1) * 6);
-    int code = unicodeAsWchar;
-    m_Buffer.push_back(prefix[nbytes - 2] | (code / order));
-    for (int i = 0; i < nbytes - 1; i++) {
-      code = code % order;
-      order >>= 6;
-      m_Buffer.push_back(0x80 | (code / order));
-    }
-  }
-}
diff --git a/core/fxcrt/cfx_utf8encoder.h b/core/fxcrt/cfx_utf8encoder.h
deleted file mode 100644
index 13815cd..0000000
--- a/core/fxcrt/cfx_utf8encoder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXCRT_CFX_UTF8ENCODER_H_
-#define CORE_FXCRT_CFX_UTF8ENCODER_H_
-
-#include <stdint.h>
-
-#include "core/fxcrt/bytestring.h"
-#include "core/fxcrt/data_vector.h"
-
-class CFX_UTF8Encoder {
- public:
-  CFX_UTF8Encoder();
-  ~CFX_UTF8Encoder();
-
-  void Input(wchar_t unicodeAsWchar);
-
-  // The data returned by GetResult() is invalidated when this is modified by
-  // appending any data.
-  ByteStringView GetResult() const {
-    return ByteStringView(m_Buffer.data(), m_Buffer.size());
-  }
-
- private:
-  DataVector<uint8_t> m_Buffer;
-};
-
-#endif  // CORE_FXCRT_CFX_UTF8ENCODER_H_
diff --git a/core/fxcrt/code_point_view.h b/core/fxcrt/code_point_view.h
new file mode 100644
index 0000000..032d4a1
--- /dev/null
+++ b/core/fxcrt/code_point_view.h
@@ -0,0 +1,91 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_CODE_POINT_VIEW_H_
+#define CORE_FXCRT_CODE_POINT_VIEW_H_
+
+#include "build/build_config.h"
+#include "core/fxcrt/string_view_template.h"
+#include "core/fxcrt/utf16.h"
+#include "third_party/base/check_op.h"
+
+namespace pdfium {
+
+#if defined(WCHAR_T_IS_UTF16)
+// A view over a UTF-16 `WideStringView` suitable for iterating by code point
+// using a range-based `for` loop.
+class CodePointView final {
+ public:
+  class Iterator {
+   public:
+    bool operator==(const Iterator& other) const {
+      return current_ == other.current_;
+    }
+
+    bool operator!=(const Iterator& other) const {
+      return current_ != other.current_;
+    }
+
+    Iterator& operator++() {
+      DCHECK_LT(current_, end_);
+      current_ += IsSupplementary(code_point_) ? 2 : 1;
+      code_point_ = Decode();
+      return *this;
+    }
+
+    char32_t operator*() const {
+      DCHECK_NE(kSentinel, code_point_);
+      return code_point_;
+    }
+
+   private:
+    friend class CodePointView;
+
+    static constexpr char32_t kSentinel = -1;
+
+    Iterator(const wchar_t* begin, const wchar_t* end)
+        : current_(begin), end_(end), code_point_(Decode()) {}
+
+    char32_t Decode() {
+      if (current_ >= end_) {
+        return kSentinel;
+      }
+
+      char32_t code_point = *current_;
+      if (IsHighSurrogate(code_point)) {
+        const wchar_t* next = current_ + 1;
+        if (next < end_ && IsLowSurrogate(*next)) {
+          code_point = SurrogatePair(code_point, *next).ToCodePoint();
+        }
+      }
+
+      return code_point;
+    }
+
+    const wchar_t* current_;
+    const wchar_t* end_;
+    char32_t code_point_;
+  };
+
+  explicit CodePointView(WideStringView backing)
+      : begin_(backing.begin()), end_(backing.end()) {
+    DCHECK_LE(begin_, end_);
+  }
+
+  Iterator begin() const { return Iterator(begin_, end_); }
+
+  Iterator end() const { return Iterator(end_, end_); }
+
+ private:
+  // Note that a `WideStringView` member would make the constructor too complex.
+  const wchar_t* begin_;
+  const wchar_t* end_;
+};
+#else
+using CodePointView = WideStringView;
+#endif  // defined(WCHAR_T_IS_UTF16)
+
+}  // namespace pdfium
+
+#endif  // CORE_FXCRT_CODE_POINT_VIEW_H_
diff --git a/core/fxcrt/code_point_view_unittest.cpp b/core/fxcrt/code_point_view_unittest.cpp
new file mode 100644
index 0000000..2fb7bd4
--- /dev/null
+++ b/core/fxcrt/code_point_view_unittest.cpp
@@ -0,0 +1,55 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/code_point_view.h"
+
+#include <string>
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using ::pdfium::CodePointView;
+
+std::u32string Materialize(CodePointView view) {
+  std::u32string materialized;
+  for (char32_t code_point : view) {
+    materialized += code_point;
+  }
+  return materialized;
+}
+
+}  // namespace
+
+TEST(CodePointViewTest, Empty) {
+  EXPECT_EQ(U"", Materialize(CodePointView(L"")));
+}
+
+TEST(CodePointViewTest, Basic) {
+  EXPECT_EQ(U"(\u0080\uffff)", Materialize(CodePointView(L"(\u0080\uffff)")));
+}
+
+TEST(CodePointViewTest, Supplementary) {
+  EXPECT_EQ(U"(🎨)", Materialize(CodePointView(L"(🎨)")));
+}
+
+TEST(CodePointViewTest, UnpairedHighSurrogate) {
+  EXPECT_EQ(U"\xd800", Materialize(CodePointView(L"\xd800")));
+}
+
+TEST(CodePointViewTest, UnpairedLowSurrogate) {
+  EXPECT_EQ(U"\xdc00", Materialize(CodePointView(L"\xdc00")));
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+TEST(CodePointViewTest, SurrogateErrorRecovery) {
+  EXPECT_EQ(U"(\xd800)", Materialize(CodePointView(L"(\xd800)"))) << "High";
+  EXPECT_EQ(U"(\xdc00)", Materialize(CodePointView(L"(\xdc00)"))) << "Low";
+  EXPECT_EQ(U"(\xd800🎨)", Materialize(CodePointView(L"(\xd800\xd83c\xdfa8)")))
+      << "High-high";
+  EXPECT_EQ(U"(🎨\xdc00)", Materialize(CodePointView(L"(\xd83c\xdfa8\xdc00)")))
+      << "Low-low";
+}
+#endif  // defined(WCHAR_T_IS_UTF16)
diff --git a/core/fxcrt/css/cfx_cssdata.cpp b/core/fxcrt/css/cfx_cssdata.cpp
index 15cd8ea..0659a59 100644
--- a/core/fxcrt/css/cfx_cssdata.cpp
+++ b/core/fxcrt/css/cfx_cssdata.cpp
@@ -18,19 +18,19 @@
 
 #undef CSS_PROP____
 #define CSS_PROP____(a, b, c, d) {CFX_CSSProperty::a, c, d},
-const CFX_CSSData::Property propertyTable[] = {
+const CFX_CSSData::Property kPropertyTable[] = {
 #include "core/fxcrt/css/properties.inc"
 };
 #undef CSS_PROP____
 
 #undef CSS_PROP_VALUE____
 #define CSS_PROP_VALUE____(a, b, c) {CFX_CSSPropertyValue::a, c},
-const CFX_CSSData::PropertyValue propertyValueTable[] = {
+const CFX_CSSData::PropertyValue kPropertyValueTable[] = {
 #include "core/fxcrt/css/property_values.inc"
 };
 #undef CSS_PROP_VALUE____
 
-const CFX_CSSData::LengthUnit lengthUnitTable[] = {
+const CFX_CSSData::LengthUnit kLengthUnitTable[] = {
     {L"cm", CFX_CSSNumberValue::Unit::kCentiMeters},
     {L"em", CFX_CSSNumberValue::Unit::kEMS},
     {L"ex", CFX_CSSNumberValue::Unit::kEXS},
@@ -42,7 +42,7 @@
 };
 
 // 16 colours from CSS 2.0 + alternate spelling of grey/gray.
-const CFX_CSSData::Color colorTable[] = {
+const CFX_CSSData::Color kColorTable[] = {
     {L"aqua", 0xff00ffff},    {L"black", 0xff000000}, {L"blue", 0xff0000ff},
     {L"fuchsia", 0xffff00ff}, {L"gray", 0xff808080},  {L"green", 0xff008000},
     {L"grey", 0xff808080},    {L"lime", 0xff00ff00},  {L"maroon", 0xff800000},
@@ -59,19 +59,21 @@
     return nullptr;
 
   uint32_t hash = FX_HashCode_GetLoweredW(name);
-  auto* result =
-      std::lower_bound(std::begin(propertyTable), std::end(propertyTable), hash,
-                       [](const CFX_CSSData::Property& iter,
-                          const uint32_t& hash) { return iter.dwHash < hash; });
+  auto* result = std::lower_bound(
+      std::begin(kPropertyTable), std::end(kPropertyTable), hash,
+      [](const CFX_CSSData::Property& iter, const uint32_t& hash) {
+        return iter.dwHash < hash;
+      });
 
-  if (result != std::end(propertyTable) && result->dwHash == hash)
+  if (result != std::end(kPropertyTable) && result->dwHash == hash) {
     return result;
+  }
   return nullptr;
 }
 
 const CFX_CSSData::Property* CFX_CSSData::GetPropertyByEnum(
     CFX_CSSProperty property) {
-  return &propertyTable[static_cast<uint8_t>(property)];
+  return &kPropertyTable[static_cast<uint8_t>(property)];
 }
 
 const CFX_CSSData::PropertyValue* CFX_CSSData::GetPropertyValueByName(
@@ -81,13 +83,14 @@
 
   uint32_t hash = FX_HashCode_GetLoweredW(wsName);
   auto* result = std::lower_bound(
-      std::begin(propertyValueTable), std::end(propertyValueTable), hash,
+      std::begin(kPropertyValueTable), std::end(kPropertyValueTable), hash,
       [](const PropertyValue& iter, const uint32_t& hash) {
         return iter.dwHash < hash;
       });
 
-  if (result != std::end(propertyValueTable) && result->dwHash == hash)
+  if (result != std::end(kPropertyValueTable) && result->dwHash == hash) {
     return result;
+  }
   return nullptr;
 }
 
@@ -99,8 +102,8 @@
   WideString lowerName = WideString(wsName);
   lowerName.MakeLower();
 
-  for (auto* iter = std::begin(lengthUnitTable);
-       iter != std::end(lengthUnitTable); ++iter) {
+  for (auto* iter = std::begin(kLengthUnitTable);
+       iter != std::end(kLengthUnitTable); ++iter) {
     if (lowerName == iter->value)
       return iter;
   }
@@ -115,7 +118,7 @@
   WideString lowerName = WideString(wsName);
   lowerName.MakeLower();
 
-  for (auto* iter = std::begin(colorTable); iter != std::end(colorTable);
+  for (auto* iter = std::begin(kColorTable); iter != std::end(kColorTable);
        ++iter) {
     if (lowerName == iter->name)
       return iter;
diff --git a/core/fxcrt/css/cfx_cssrulecollection.cpp b/core/fxcrt/css/cfx_cssrulecollection.cpp
index 4abdba4..89b6852 100644
--- a/core/fxcrt/css/cfx_cssrulecollection.cpp
+++ b/core/fxcrt/css/cfx_cssrulecollection.cpp
@@ -44,3 +44,5 @@
 CFX_CSSRuleCollection::Data::Data(CFX_CSSSelector* pSel,
                                   CFX_CSSDeclaration* pDecl)
     : pSelector(pSel), pDeclaration(pDecl) {}
+
+CFX_CSSRuleCollection::Data::~Data() = default;
diff --git a/core/fxcrt/css/cfx_cssrulecollection.h b/core/fxcrt/css/cfx_cssrulecollection.h
index b5f09ad..125fdd2 100644
--- a/core/fxcrt/css/cfx_cssrulecollection.h
+++ b/core/fxcrt/css/cfx_cssrulecollection.h
@@ -11,6 +11,7 @@
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
 
 class CFX_CSSDeclaration;
@@ -23,9 +24,10 @@
   class Data {
    public:
     Data(CFX_CSSSelector* pSel, CFX_CSSDeclaration* pDecl);
+    ~Data();
 
-    CFX_CSSSelector* const pSelector;
-    CFX_CSSDeclaration* const pDeclaration;
+    UnownedPtr<CFX_CSSSelector> const pSelector;
+    UnownedPtr<CFX_CSSDeclaration> const pDeclaration;
   };
 
   CFX_CSSRuleCollection();
diff --git a/core/fxcrt/css/cfx_csssyntaxparser.cpp b/core/fxcrt/css/cfx_csssyntaxparser.cpp
index 9341798..e5a4d41 100644
--- a/core/fxcrt/css/cfx_csssyntaxparser.cpp
+++ b/core/fxcrt/css/cfx_csssyntaxparser.cpp
@@ -10,7 +10,6 @@
 #include "core/fxcrt/css/cfx_cssdeclaration.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
-#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -149,9 +148,6 @@
         }
         m_Input.MoveNext();
         break;
-      default:
-        NOTREACHED();
-        break;
     }
   }
   if (m_eMode == Mode::kPropertyValue && !m_Output.IsEmpty())
diff --git a/core/fxcrt/fixed_size_data_vector.h b/core/fxcrt/fixed_size_data_vector.h
index c0adc8c..bec5e10 100644
--- a/core/fxcrt/fixed_size_data_vector.h
+++ b/core/fxcrt/fixed_size_data_vector.h
@@ -11,7 +11,7 @@
 #include <utility>
 
 #include "core/fxcrt/fx_memory_wrappers.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
diff --git a/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
index e80ba27..613d838 100644
--- a/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
+++ b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
@@ -12,7 +12,7 @@
 #include "core/fxcrt/span_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(FixedTryAllocZeroedDataVector, NoData) {
   FixedTryAllocZeroedDataVector<int> vec;
diff --git a/core/fxcrt/fixed_uninit_data_vector_unittest.cpp b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
index d7a63da..70eda6a 100644
--- a/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
+++ b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
@@ -11,7 +11,7 @@
 #include "core/fxcrt/span_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(FixedUninitDataVector, NoData) {
   FixedUninitDataVector<int> vec;
diff --git a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
index ac024bb..297818f 100644
--- a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
+++ b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
@@ -11,7 +11,7 @@
 #include "core/fxcrt/span_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 TEST(FixedZeroedDataVector, NoData) {
   FixedZeroedDataVector<int> vec;
diff --git a/core/fxcrt/fx_codepage.h b/core/fxcrt/fx_codepage.h
index 94e764e..2aa7741 100644
--- a/core/fxcrt/fx_codepage.h
+++ b/core/fxcrt/fx_codepage.h
@@ -12,7 +12,8 @@
 // Prove consistency with incomplete forward definitions.
 #include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/span.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/containers/span.h"
 
 enum class FX_CodePage : uint16_t {
   kDefANSI = 0,
@@ -103,7 +104,7 @@
 // Hi-bytes to unicode codepoint mapping for various code pages.
 struct FX_CharsetUnicodes {
   FX_Charset m_Charset;
-  const uint16_t* m_pUnicodes;  // Raw, POD struct.
+  UNOWNED_PTR_EXCLUSION const uint16_t* m_pUnicodes;  // POD struct.
 };
 
 extern const FX_CharsetUnicodes kFX_CharsetUnicodes[8];
diff --git a/core/fxcrt/fx_coordinates.h b/core/fxcrt/fx_coordinates.h
index abc87ff..f6b2347 100644
--- a/core/fxcrt/fx_coordinates.h
+++ b/core/fxcrt/fx_coordinates.h
@@ -13,22 +13,17 @@
 #include <iosfwd>
 #endif
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 template <class BaseType>
 class CFX_PTemplate {
  public:
-  CFX_PTemplate() : x(0), y(0) {}
-  CFX_PTemplate(BaseType new_x, BaseType new_y) : x(new_x), y(new_y) {}
-  CFX_PTemplate(const CFX_PTemplate& other) : x(other.x), y(other.y) {}
+  constexpr CFX_PTemplate() = default;
+  constexpr CFX_PTemplate(BaseType new_x, BaseType new_y)
+      : x(new_x), y(new_y) {}
+  CFX_PTemplate(const CFX_PTemplate& other) = default;
+  CFX_PTemplate& operator=(const CFX_PTemplate& other) = default;
 
-  CFX_PTemplate& operator=(const CFX_PTemplate& other) {
-    if (this != &other) {
-      x = other.x;
-      y = other.y;
-    }
-    return *this;
-  }
   bool operator==(const CFX_PTemplate& other) const {
     return x == other.x && y == other.y;
   }
@@ -52,8 +47,8 @@
     return CFX_PTemplate(x - other.x, y - other.y);
   }
 
-  BaseType x;
-  BaseType y;
+  BaseType x = 0;
+  BaseType y = 0;
 };
 using CFX_Point16 = CFX_PTemplate<int16_t>;
 using CFX_Point = CFX_PTemplate<int32_t>;
@@ -62,13 +57,11 @@
 template <class BaseType>
 class CFX_STemplate {
  public:
-  CFX_STemplate() : width(0), height(0) {}
-
-  CFX_STemplate(BaseType new_width, BaseType new_height)
+  constexpr CFX_STemplate() = default;
+  constexpr CFX_STemplate(BaseType new_width, BaseType new_height)
       : width(new_width), height(new_height) {}
-
-  CFX_STemplate(const CFX_STemplate& other)
-      : width(other.width), height(other.height) {}
+  CFX_STemplate(const CFX_STemplate& other) = default;
+  CFX_STemplate& operator=(const CFX_STemplate& other) = default;
 
   template <typename OtherType>
   CFX_STemplate<OtherType> As() const {
@@ -80,13 +73,6 @@
     width = 0;
     height = 0;
   }
-  CFX_STemplate& operator=(const CFX_STemplate& other) {
-    if (this != &other) {
-      width = other.width;
-      height = other.height;
-    }
-    return *this;
-  }
   bool operator==(const CFX_STemplate& other) const {
     return width == other.width && height == other.height;
   }
@@ -126,8 +112,8 @@
     return CFX_STemplate(width / divisor, height / divisor);
   }
 
-  BaseType width;
-  BaseType height;
+  BaseType width = 0;
+  BaseType height = 0;
 };
 using CFX_Size = CFX_STemplate<int32_t>;
 using CFX_SizeF = CFX_STemplate<float>;
@@ -160,8 +146,11 @@
 // LTRB rectangles (y-axis runs downwards).
 // Struct layout is compatible with win32 RECT.
 struct FX_RECT {
-  FX_RECT() = default;
-  FX_RECT(int l, int t, int r, int b) : left(l), top(t), right(r), bottom(b) {}
+  constexpr FX_RECT() = default;
+  constexpr FX_RECT(int l, int t, int r, int b)
+      : left(l), top(t), right(r), bottom(b) {}
+  FX_RECT(const FX_RECT& that) = default;
+  FX_RECT& operator=(const FX_RECT& that) = default;
 
   int Width() const { return right - left; }
   int Height() const { return bottom - top; }
@@ -202,6 +191,8 @@
   constexpr CFX_FloatRect() = default;
   constexpr CFX_FloatRect(float l, float b, float r, float t)
       : left(l), bottom(b), right(r), top(t) {}
+  CFX_FloatRect(const CFX_FloatRect& that) = default;
+  CFX_FloatRect& operator=(const CFX_FloatRect& that) = default;
 
   explicit CFX_FloatRect(const FX_RECT& rect);
   explicit CFX_FloatRect(const CFX_PointF& point);
@@ -295,9 +286,15 @@
   using PointType = CFX_PointF;
   using SizeType = CFX_SizeF;
 
-  CFX_RectF() = default;
-  CFX_RectF(float dst_left, float dst_top, float dst_width, float dst_height)
+  constexpr CFX_RectF() = default;
+  constexpr CFX_RectF(float dst_left,
+                      float dst_top,
+                      float dst_width,
+                      float dst_height)
       : left(dst_left), top(dst_top), width(dst_width), height(dst_height) {}
+  CFX_RectF(const CFX_RectF& other) = default;
+  CFX_RectF& operator=(const CFX_RectF& other) = default;
+
   CFX_RectF(float dst_left, float dst_top, const SizeType& dst_size)
       : left(dst_left),
         top(dst_top),
@@ -313,11 +310,6 @@
         width(static_cast<float>(that.Width())),
         height(static_cast<float>(that.Height())) {}
 
-  // NOLINTNEXTLINE(runtime/explicit)
-  CFX_RectF(const CFX_RectF& other) = default;
-
-  CFX_RectF& operator=(const CFX_RectF& other) = default;
-
   CFX_RectF& operator+=(const PointType& p) {
     left += p.x;
     top += p.y;
diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp
index c682259..dc312f0 100644
--- a/core/fxcrt/fx_extension.cpp
+++ b/core/fxcrt/fx_extension.cpp
@@ -10,6 +10,7 @@
 #include <limits>
 
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/utf16.h"
 #include "third_party/base/check.h"
 
 namespace {
@@ -150,16 +151,17 @@
 }
 
 size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf) {
-  DCHECK(unicode <= 0xD7FF || (unicode > 0xDFFF && unicode <= 0x10FFFF));
+  DCHECK(unicode <= pdfium::kMaximumSupplementaryCodePoint);
+  DCHECK(!pdfium::IsHighSurrogate(unicode));
+  DCHECK(!pdfium::IsLowSurrogate(unicode));
+
   if (unicode <= 0xFFFF) {
     FXSYS_IntToFourHexChars(unicode, buf);
     return 4;
   }
-  unicode -= 0x010000;
-  // High ten bits plus 0xD800
-  FXSYS_IntToFourHexChars(0xD800 + unicode / 0x400, buf);
-  // Low ten bits plus 0xDC00
-  FXSYS_IntToFourHexChars(0xDC00 + unicode % 0x400, buf + 4);
+  pdfium::SurrogatePair surrogate_pair(unicode);
+  FXSYS_IntToFourHexChars(surrogate_pair.high(), buf);
+  FXSYS_IntToFourHexChars(surrogate_pair.low(), buf + 4);
   return 8;
 }
 
diff --git a/core/fxcrt/fx_folder_posix.cpp b/core/fxcrt/fx_folder_posix.cpp
index b4cf9f9..fb873be 100644
--- a/core/fxcrt/fx_folder_posix.cpp
+++ b/core/fxcrt/fx_folder_posix.cpp
@@ -10,7 +10,7 @@
 
 #include "build/build_config.h"
 #include "core/fxcrt/unowned_ptr.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 #if BUILDFLAG(IS_WIN)
 #error "built on wrong platform"
diff --git a/core/fxcrt/fx_folder_windows.cpp b/core/fxcrt/fx_folder_windows.cpp
index 500c733..6b19dde 100644
--- a/core/fxcrt/fx_folder_windows.cpp
+++ b/core/fxcrt/fx_folder_windows.cpp
@@ -9,7 +9,7 @@
 #include <memory>
 
 #include "build/build_config.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 #if !BUILDFLAG(IS_WIN)
 #error "built on wrong platform"
diff --git a/core/fxcrt/fx_memcpy_wrappers.h b/core/fxcrt/fx_memcpy_wrappers.h
new file mode 100644
index 0000000..2dc8c1c
--- /dev/null
+++ b/core/fxcrt/fx_memcpy_wrappers.h
@@ -0,0 +1,86 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FXCRT_FX_MEMCPY_WRAPPERS_H_
+#define CORE_FXCRT_FX_MEMCPY_WRAPPERS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <wchar.h>
+
+// Wrappers to avoid the zero-length w/NULL arg gotchas in C spec. Use these
+// if there is a possibility of a NULL arg (or a bad arg) that is to be ignored
+// when the length is zero, otherwise just call the C Run Time Library function
+// itself.
+inline int FXSYS_memcmp(const void* ptr1, const void* ptr2, size_t len) {
+  return len ? memcmp(ptr1, ptr2, len) : 0;
+}
+
+inline int FXSYS_wmemcmp(const wchar_t* ptr1, const wchar_t* ptr2, size_t len) {
+  return len ? wmemcmp(ptr1, ptr2, len) : 0;
+}
+
+inline void* FXSYS_memcpy(void* ptr1, const void* ptr2, size_t len) {
+  return len ? memcpy(ptr1, ptr2, len) : ptr1;
+}
+
+inline wchar_t* FXSYS_wmemcpy(wchar_t* ptr1, const wchar_t* ptr2, size_t len) {
+  return len ? wmemcpy(ptr1, ptr2, len) : ptr1;
+}
+
+inline void* FXSYS_memmove(void* ptr1, const void* ptr2, size_t len) {
+  return len ? memmove(ptr1, ptr2, len) : ptr1;
+}
+
+inline wchar_t* FXSYS_wmemmove(wchar_t* ptr1, const wchar_t* ptr2, size_t len) {
+  return len ? wmemmove(ptr1, ptr2, len) : ptr1;
+}
+
+inline void* FXSYS_memset(void* ptr1, int val, size_t len) {
+  return len ? memset(ptr1, val, len) : ptr1;
+}
+
+inline wchar_t* FXSYS_wmemset(wchar_t* ptr1, int val, size_t len) {
+  return len ? wmemset(ptr1, val, len) : ptr1;
+}
+
+inline const void* FXSYS_memchr(const void* ptr1, int val, size_t len) {
+  return len ? memchr(ptr1, val, len) : nullptr;
+}
+
+inline const wchar_t* FXSYS_wmemchr(const wchar_t* ptr1,
+                                    wchar_t val,
+                                    size_t len) {
+  return len ? wmemchr(ptr1, val, len) : nullptr;
+}
+
+// Overloaded functions for C++ templates
+inline size_t FXSYS_len(const char* ptr) {
+  return strlen(ptr);
+}
+
+inline size_t FXSYS_len(const wchar_t* ptr) {
+  return wcslen(ptr);
+}
+
+inline int FXSYS_cmp(const char* ptr1, const char* ptr2, size_t len) {
+  return FXSYS_memcmp(ptr1, ptr2, len);
+}
+
+inline int FXSYS_cmp(const wchar_t* ptr1, const wchar_t* ptr2, size_t len) {
+  return FXSYS_wmemcmp(ptr1, ptr2, len);
+}
+
+inline const char* FXSYS_chr(const char* ptr, char ch, size_t len) {
+  return reinterpret_cast<const char*>(FXSYS_memchr(ptr, ch, len));
+}
+
+inline const wchar_t* FXSYS_chr(const wchar_t* ptr, wchar_t ch, size_t len) {
+  return FXSYS_wmemchr(ptr, ch, len);
+}
+
+#endif  // CORE_FXCRT_FX_MEMCPY_WRAPPERS_H_
diff --git a/core/fxcrt/fx_memcpy_wrappers_unittest.cpp b/core/fxcrt/fx_memcpy_wrappers_unittest.cpp
new file mode 100644
index 0000000..9d29050
--- /dev/null
+++ b/core/fxcrt/fx_memcpy_wrappers_unittest.cpp
@@ -0,0 +1,57 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fx_memcpy_wrappers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, FXSYS_memset) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_memset(nullptr, 0, 0));
+}
+
+TEST(fxcrt, FXSYS_wmemset) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_wmemset(nullptr, 0, 0));
+}
+
+TEST(fxcrt, FXSYS_memcpy) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_memcpy(nullptr, nullptr, 0));
+}
+
+TEST(fxcrt, FXSYS_wmemcpy) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_wmemcpy(nullptr, nullptr, 0));
+}
+
+TEST(fxcrt, FXSYS_memmove) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_memmove(nullptr, nullptr, 0));
+}
+
+TEST(fxcrt, FXSYS_wmemmove) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_wmemmove(nullptr, nullptr, 0));
+}
+
+TEST(fxcrt, FXSYS_memcmp) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(0, FXSYS_memcmp(nullptr, nullptr, 0));
+}
+
+TEST(fxcrt, FXSYS_wmemcmp) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(0, FXSYS_wmemcmp(nullptr, nullptr, 0));
+}
+
+TEST(fxcrt, FXSYS_memchr) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_memchr(nullptr, 0, 0));
+}
+
+TEST(fxcrt, FXSYS_wmemchr) {
+  // Test passes if it does not trigger UBSAN warnings.
+  EXPECT_EQ(nullptr, FXSYS_wmemchr(nullptr, 0, 0));
+}
diff --git a/core/fxcrt/fx_memory.h b/core/fxcrt/fx_memory.h
index 2ae5003..ac61146 100644
--- a/core/fxcrt/fx_memory.h
+++ b/core/fxcrt/fx_memory.h
@@ -50,14 +50,17 @@
 #define FX_AllocUninit2D(type, w, h) \
   static_cast<type*>(pdfium::internal::AllocOrDie2D(w, h, sizeof(type)))
 
+// FX_Free frees memory from the above.
+#define FX_Free(ptr) pdfium::internal::Dealloc(ptr)
+
 // String Partition Allocators.
 
 // This never returns nullptr, but returns uninitialized memory.
 #define FX_StringAlloc(type, size) \
   static_cast<type*>(pdfium::internal::StringAllocOrDie(size, sizeof(type)))
 
-// FX_Free accepts memory from all of the above.
-void FX_Free(void* ptr);
+// FX_StringFree frees memory from FX_StringAlloc.
+#define FX_StringFree(ptr) pdfium::internal::StringDealloc(ptr)
 
 #ifndef V8_ENABLE_SANDBOX
 // V8 Array Buffer Partition Allocators.
@@ -84,10 +87,12 @@
 void* CallocOrDie(size_t num_members, size_t member_size);
 void* CallocOrDie2D(size_t w, size_t h, size_t member_size);
 void* ReallocOrDie(void* ptr, size_t num_members, size_t member_size);
+void Dealloc(void* ptr);
 
 // String partition.
 void* StringAlloc(size_t num_members, size_t member_size);
 void* StringAllocOrDie(size_t num_members, size_t member_size);
+void StringDealloc(void* ptr);
 
 }  // namespace internal
 }  // namespace pdfium
diff --git a/core/fxcrt/fx_memory_malloc.cpp b/core/fxcrt/fx_memory_malloc.cpp
index 44a05c6..e199d42 100644
--- a/core/fxcrt/fx_memory_malloc.cpp
+++ b/core/fxcrt/fx_memory_malloc.cpp
@@ -47,6 +47,10 @@
   return realloc(ptr, total.ValueOrDie());
 }
 
+void Dealloc(void* ptr) {
+  free(ptr);
+}
+
 void* StringAlloc(size_t num_members, size_t member_size) {
   FX_SAFE_SIZE_T total = member_size;
   total *= num_members;
@@ -55,6 +59,10 @@
   return malloc(total.ValueOrDie());
 }
 
+void StringDealloc(void* ptr) {
+  free(ptr);
+}
+
 }  // namespace internal
 }  // namespace pdfium
 
@@ -77,7 +85,3 @@
 void FX_ArrayBufferFree(void* data) {
   free(data);
 }
-
-void FX_Free(void* ptr) {
-  free(ptr);
-}
diff --git a/core/fxcrt/fx_memory_pa.cpp b/core/fxcrt/fx_memory_pa.cpp
index 35c043a..789f68c 100644
--- a/core/fxcrt/fx_memory_pa.cpp
+++ b/core/fxcrt/fx_memory_pa.cpp
@@ -16,33 +16,25 @@
 
 namespace {
 
-constexpr partition_alloc::PartitionOptions kOptions = {
-    partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed,
-    partition_alloc::PartitionOptions::ThreadCache::kDisabled,
-    partition_alloc::PartitionOptions::Quarantine::kDisallowed,
-    partition_alloc::PartitionOptions::Cookie::kAllowed,
-    partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
-    partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled,
-    partition_alloc::PartitionOptions::UseConfigurablePool::kNo,
-};
+constexpr partition_alloc::PartitionOptions kOptions = {};
 
 #ifndef V8_ENABLE_SANDBOX
 partition_alloc::PartitionAllocator& GetArrayBufferPartitionAllocator() {
   static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
-      s_array_buffer_allocator;
+      s_array_buffer_allocator(kOptions);
   return *s_array_buffer_allocator;
 }
 #endif  //  V8_ENABLE_SANDBOX
 
 partition_alloc::PartitionAllocator& GetGeneralPartitionAllocator() {
   static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
-      s_general_allocator;
+      s_general_allocator(kOptions);
   return *s_general_allocator;
 }
 
 partition_alloc::PartitionAllocator& GetStringPartitionAllocator() {
   static pdfium::base::NoDestructor<partition_alloc::PartitionAllocator>
-      s_string_allocator;
+      s_string_allocator(kOptions);
   return *s_string_allocator;
 }
 
@@ -85,6 +77,20 @@
       "GeneralPartition");
 }
 
+void Dealloc(void* ptr) {
+  // TODO(palmer): Removing this check exposes crashes when PDFium callers
+  // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
+  // other Partition Alloc callers need this tolerant behavior. Additionally,
+  // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
+  // not have to have that.
+  //
+  // So this check is hiding (what I consider to be) bugs, and we should try to
+  // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
+  if (ptr) {
+    GetGeneralPartitionAllocator().root()->Free(ptr);
+  }
+}
+
 void* StringAlloc(size_t num_members, size_t member_size) {
   FX_SAFE_SIZE_T total = member_size;
   total *= num_members;
@@ -96,6 +102,20 @@
       "StringPartition");
 }
 
+void StringDealloc(void* ptr) {
+  // TODO(palmer): Removing this check exposes crashes when PDFium callers
+  // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
+  // other Partition Alloc callers need this tolerant behavior. Additionally,
+  // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
+  // not have to have that.
+  //
+  // So this check is hiding (what I consider to be) bugs, and we should try to
+  // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
+  if (ptr) {
+    GetStringPartitionAllocator().root()->Free(ptr);
+  }
+}
+
 }  // namespace internal
 }  // namespace pdfium
 
@@ -103,11 +123,13 @@
   static bool s_partition_allocators_initialized = false;
   if (!s_partition_allocators_initialized) {
     partition_alloc::PartitionAllocGlobalInit(FX_OutOfMemoryTerminate);
+    // These calls force the allocators to be created and initialized (via magic
+    // of static local variables).
 #ifndef V8_ENABLE_SANDBOX
-    GetArrayBufferPartitionAllocator().init(kOptions);
+    GetArrayBufferPartitionAllocator();
 #endif  // V8_ENABLE_SANDBOX
-    GetGeneralPartitionAllocator().init(kOptions);
-    GetStringPartitionAllocator().init(kOptions);
+    GetGeneralPartitionAllocator();
+    GetStringPartitionAllocator();
     s_partition_allocators_initialized = true;
   }
 }
@@ -127,16 +149,3 @@
   GetArrayBufferPartitionAllocator().root()->Free(data);
 }
 #endif  // V8_ENABLE_SANDBOX
-
-void FX_Free(void* ptr) {
-  // TODO(palmer): Removing this check exposes crashes when PDFium callers
-  // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no
-  // other Partition Alloc callers need this tolerant behavior. Additionally,
-  // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to
-  // not have to have that.
-  //
-  // So this check is hiding (what I consider to be) bugs, and we should try to
-  // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690
-  if (ptr)
-    partition_alloc::ThreadSafePartitionRoot::Free(ptr);
-}
diff --git a/core/fxcrt/fx_memory_unittest.cpp b/core/fxcrt/fx_memory_unittest.cpp
index e0de4f4..7d3fe4d 100644
--- a/core/fxcrt/fx_memory_unittest.cpp
+++ b/core/fxcrt/fx_memory_unittest.cpp
@@ -5,10 +5,15 @@
 #include "core/fxcrt/fx_memory.h"
 
 #include <limits>
+#include <memory>
 
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "base/allocator/partition_allocator/partition_address_space.h"
+#endif
+
 namespace {
 
 constexpr size_t kMaxByteAlloc = std::numeric_limits<size_t>::max();
@@ -125,3 +130,39 @@
   EXPECT_EQ(512, FxAlignToBoundary<512>(i512));
   EXPECT_EQ(-512, FxAlignToBoundary<512>(ineg));
 }
+
+#if defined(PDF_USE_PARTITION_ALLOC)
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(HAS_64_BIT_POINTERS)
+TEST(FxMemory, NewOperatorResultIsPA) {
+  auto obj = std::make_unique<double>(4.0);
+  EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
+      reinterpret_cast<uintptr_t>(obj.get())));
+#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+  EXPECT_TRUE(partition_alloc::IsManagedByPartitionAllocBRPPool(
+      reinterpret_cast<uintptr_t>(obj.get())));
+#endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+}
+
+TEST(FxMemory, MallocResultIsPA) {
+  void* obj = malloc(16);
+  EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
+      reinterpret_cast<uintptr_t>(obj)));
+#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+  EXPECT_TRUE(partition_alloc::IsManagedByPartitionAllocBRPPool(
+      reinterpret_cast<uintptr_t>(obj)));
+#endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+  free(obj);
+}
+
+TEST(FxMemory, StackObjectIsNotPA) {
+  int x = 3;
+  EXPECT_FALSE(partition_alloc::IsManagedByPartitionAlloc(
+      reinterpret_cast<uintptr_t>(&x)));
+#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+  EXPECT_FALSE(partition_alloc::IsManagedByPartitionAllocBRPPool(
+      reinterpret_cast<uintptr_t>(&x)));
+#endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+}
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
+        // BUILDFLAG(HAS_64_BIT_POINTERS)
+#endif  // defined(PDF_USE_PARTITION_ALLOC)
diff --git a/core/fxcrt/fx_memory_wrappers.h b/core/fxcrt/fx_memory_wrappers.h
index 9e87754..f4f0b06 100644
--- a/core/fxcrt/fx_memory_wrappers.h
+++ b/core/fxcrt/fx_memory_wrappers.h
@@ -30,7 +30,7 @@
 // Allocators for mapping STL containers onto Partition Alloc.
 // Otherwise, replacing e.g. the FX_AllocUninit/FX_Free pairs with STL may
 // undo some of the nice segregation that we get from PartitionAlloc.
-template <class T, void* F(size_t, size_t)>
+template <class T, void* Alloc(size_t, size_t), void Free(void*)>
 struct FxPartitionAllocAllocator {
  public:
 #if !defined(COMPILER_MSVC) || defined(NDEBUG)
@@ -49,7 +49,7 @@
 
   template <class U>
   struct rebind {
-    using other = FxPartitionAllocAllocator<U, F>;
+    using other = FxPartitionAllocAllocator<U, Alloc, Free>;
   };
 
   FxPartitionAllocAllocator() noexcept = default;
@@ -59,14 +59,14 @@
 
   template <typename U>
   FxPartitionAllocAllocator(
-      const FxPartitionAllocAllocator<U, F>& other) noexcept {}
+      const FxPartitionAllocAllocator<U, Alloc, Free>& other) noexcept {}
 
   pointer address(reference x) const noexcept { return &x; }
   const_pointer address(const_reference x) const noexcept { return &x; }
   pointer allocate(size_type n, const void* hint = 0) {
-    return static_cast<pointer>(F(n, sizeof(value_type)));
+    return static_cast<pointer>(Alloc(n, sizeof(value_type)));
   }
-  void deallocate(pointer p, size_type n) { FX_Free(p); }
+  void deallocate(pointer p, size_type n) { Free(p); }
   size_type max_size() const noexcept {
     return std::numeric_limits<size_type>::max() / sizeof(value_type);
   }
@@ -92,13 +92,16 @@
           typename = std::enable_if_t<std::is_arithmetic<T>::value ||
                                       std::is_enum<T>::value ||
                                       IsFXDataPartitionException<T>::value>>
-using FxAllocAllocator =
-    FxPartitionAllocAllocator<T, pdfium::internal::AllocOrDie>;
+using FxAllocAllocator = FxPartitionAllocAllocator<T,
+                                                   pdfium::internal::AllocOrDie,
+                                                   pdfium::internal::Dealloc>;
 
 // Used to put backing store for std::string<> and std::ostringstream<>
 // into the string partition.
 template <typename T>
 using FxStringAllocator =
-    FxPartitionAllocAllocator<T, pdfium::internal::StringAllocOrDie>;
+    FxPartitionAllocAllocator<T,
+                              pdfium::internal::StringAllocOrDie,
+                              pdfium::internal::StringDealloc>;
 
 #endif  // CORE_FXCRT_FX_MEMORY_WRAPPERS_H_
diff --git a/core/fxcrt/fx_number.cpp b/core/fxcrt/fx_number.cpp
index a0f38e6..d577531 100644
--- a/core/fxcrt/fx_number.cpp
+++ b/core/fxcrt/fx_number.cpp
@@ -13,6 +13,7 @@
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_string.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 FX_Number::FX_Number()
     : m_bIsInteger(true), m_bIsSigned(false), m_UnsignedValue(0) {}
@@ -88,7 +89,11 @@
 }
 
 int32_t FX_Number::GetSigned() const {
-  return m_bIsInteger ? m_SignedValue : static_cast<int32_t>(m_FloatValue);
+  if (m_bIsInteger) {
+    return m_SignedValue;
+  }
+
+  return pdfium::base::saturated_cast<int32_t>(m_FloatValue);
 }
 
 float FX_Number::GetFloat() const {
diff --git a/core/fxcrt/fx_number_unittest.cpp b/core/fxcrt/fx_number_unittest.cpp
index deb4cf3..9a1d2eb 100644
--- a/core/fxcrt/fx_number_unittest.cpp
+++ b/core/fxcrt/fx_number_unittest.cpp
@@ -43,6 +43,18 @@
   EXPECT_TRUE(number2.IsSigned());
   EXPECT_EQ(-100, number2.GetSigned());
   EXPECT_FLOAT_EQ(-100.001f, number2.GetFloat());
+
+  // Show positive saturation.
+  FX_Number number3(1e17f);
+  EXPECT_FALSE(number3.IsInteger());
+  EXPECT_TRUE(number3.IsSigned());
+  EXPECT_EQ(std::numeric_limits<int32_t>::max(), number3.GetSigned());
+
+  // Show negative saturation.
+  FX_Number number4(-1e17f);
+  EXPECT_FALSE(number4.IsInteger());
+  EXPECT_TRUE(number4.IsSigned());
+  EXPECT_EQ(std::numeric_limits<int32_t>::min(), number4.GetSigned());
 }
 
 TEST(fxnumber, FromStringUnsigned) {
diff --git a/core/fxcrt/fx_stream.h b/core/fxcrt/fx_stream.h
index e6f1f55..15e02d5 100644
--- a/core/fxcrt/fx_stream.h
+++ b/core/fxcrt/fx_stream.h
@@ -13,7 +13,7 @@
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_types.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class IFX_WriteStream {
  public:
diff --git a/core/fxcrt/fx_string.cpp b/core/fxcrt/fx_string.cpp
index 76773a8..62109cd 100644
--- a/core/fxcrt/fx_string.cpp
+++ b/core/fxcrt/fx_string.cpp
@@ -6,26 +6,125 @@
 
 #include "core/fxcrt/fx_string.h"
 
+#include <stdint.h>
+
 #include <iterator>
 
-#include "core/fxcrt/cfx_utf8decoder.h"
-#include "core/fxcrt/cfx_utf8encoder.h"
+#include "build/build_config.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/code_point_view.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/span_util.h"
+#include "core/fxcrt/string_view_template.h"
+#include "core/fxcrt/utf16.h"
+#include "core/fxcrt/widestring.h"
 #include "third_party/base/compiler_specific.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
+
+namespace {
+
+// Appends a Unicode code point to a `ByteString` using UTF-8.
+//
+// TODO(crbug.com/pdfium/2041): Migrate to `ByteString`.
+void AppendCodePointToByteString(char32_t code_point, ByteString& buffer) {
+  if (code_point > pdfium::kMaximumSupplementaryCodePoint) {
+    // Invalid code point above U+10FFFF.
+    return;
+  }
+
+  if (code_point < 0x80) {
+    // 7-bit code points are unchanged in UTF-8.
+    buffer += code_point;
+    return;
+  }
+
+  int byte_size;
+  if (code_point < 0x800) {
+    byte_size = 2;
+  } else if (code_point < 0x10000) {
+    byte_size = 3;
+  } else {
+    byte_size = 4;
+  }
+
+  static constexpr uint8_t kPrefix[] = {0xc0, 0xe0, 0xf0};
+  int order = 1 << ((byte_size - 1) * 6);
+  buffer += kPrefix[byte_size - 2] | (code_point / order);
+  for (int i = 0; i < byte_size - 1; i++) {
+    code_point = code_point % order;
+    order >>= 6;
+    buffer += 0x80 | (code_point / order);
+  }
+}
+
+// Appends a Unicode code point to a `WideString` using either UTF-16 or UTF-32,
+// depending on the platform's definition of `wchar_t`.
+//
+// TODO(crbug.com/pdfium/2031): Always use UTF-16.
+// TODO(crbug.com/pdfium/2041): Migrate to `WideString`.
+void AppendCodePointToWideString(char32_t code_point, WideString& buffer) {
+  if (code_point > pdfium::kMaximumSupplementaryCodePoint) {
+    // Invalid code point above U+10FFFF.
+    return;
+  }
+
+#if defined(WCHAR_T_IS_UTF16)
+  if (code_point < pdfium::kMinimumSupplementaryCodePoint) {
+    buffer += static_cast<wchar_t>(code_point);
+  } else {
+    // Encode as UTF-16 surrogate pair.
+    pdfium::SurrogatePair surrogate_pair(code_point);
+    buffer += surrogate_pair.high();
+    buffer += surrogate_pair.low();
+  }
+#else
+  buffer += static_cast<wchar_t>(code_point);
+#endif  // defined(WCHAR_T_IS_UTF16)
+}
+
+}  // namespace
 
 ByteString FX_UTF8Encode(WideStringView wsStr) {
-  CFX_UTF8Encoder encoder;
-  for (size_t i = 0; i < wsStr.GetLength(); ++i)
-    encoder.Input(wsStr[i]);
-
-  return ByteString(encoder.GetResult());
+  ByteString buffer;
+  for (char32_t code_point : pdfium::CodePointView(wsStr)) {
+    AppendCodePointToByteString(code_point, buffer);
+  }
+  return buffer;
 }
 
 WideString FX_UTF8Decode(ByteStringView bsStr) {
-  CFX_UTF8Decoder decoder(bsStr);
-  return decoder.TakeResult();
+  WideString buffer;
+
+  int remaining = 0;
+  char32_t code_point = 0;
+  for (char byte : bsStr) {
+    uint8_t code_unit = static_cast<uint8_t>(byte);
+    if (code_unit < 0x80) {
+      remaining = 0;
+      AppendCodePointToWideString(code_unit, buffer);
+    } else if (code_unit < 0xc0) {
+      if (remaining > 0) {
+        --remaining;
+        code_point = (code_point << 6) | (code_unit & 0x3f);
+        if (remaining == 0) {
+          AppendCodePointToWideString(code_point, buffer);
+        }
+      }
+    } else if (code_unit < 0xe0) {
+      remaining = 1;
+      code_point = code_unit & 0x1f;
+    } else if (code_unit < 0xf0) {
+      remaining = 2;
+      code_point = code_unit & 0x0f;
+    } else if (code_unit < 0xf8) {
+      remaining = 3;
+      code_point = code_unit & 0x07;
+    } else {
+      remaining = 0;
+    }
+  }
+
+  return buffer;
 }
 
 namespace {
diff --git a/core/fxcrt/fx_string.h b/core/fxcrt/fx_string.h
index 41d73e4..49351c1 100644
--- a/core/fxcrt/fx_string.h
+++ b/core/fxcrt/fx_string.h
@@ -13,7 +13,7 @@
 
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/widestring.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 constexpr uint32_t FXBSTR_ID(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) {
   return static_cast<uint32_t>(c1) << 24 | static_cast<uint32_t>(c2) << 16 |
diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp
index 2619864..c671d22 100644
--- a/core/fxcrt/fx_string_unittest.cpp
+++ b/core/fxcrt/fx_string_unittest.cpp
@@ -4,9 +4,11 @@
 
 #include <limits>
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/utf16.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 char* TerminatedFloatToString(float value, pdfium::span<char> buf) {
   size_t buflen = FloatToString(value, buf);
@@ -20,52 +22,135 @@
   return buf.data();
 }
 
-TEST(fxstring, FX_UTF8Encode) {
+TEST(fxstring, FXUTF8Encode) {
   EXPECT_EQ("", FX_UTF8Encode(WideStringView()));
   EXPECT_EQ(
       "x"
-      "\xc2\x80"
-      "\xc3\xbf"
-      "\xef\xbc\xac"
+      "\u0080"
+      "\u00ff"
+      "\ud7ff"
+      "\ue000"
+      "\uff2c"
+      "\uffff"
       "y",
       FX_UTF8Encode(L"x"
                     L"\u0080"
                     L"\u00ff"
+                    L"\ud7ff"
+                    L"\ue000"
                     L"\uff2c"
+                    L"\uffff"
                     L"y"));
 }
 
-TEST(fxstring, FX_UTF8Decode) {
+TEST(fxstring, FXUTF8EncodeSupplementary) {
+  EXPECT_EQ(
+      "\U00010000"
+      "🎨"
+      "\U0010ffff",
+      FX_UTF8Encode(L"\U00010000"
+                    L"\U0001f3a8"
+                    L"\U0010ffff"));
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+TEST(fxstring, FXUTF8EncodeSurrogateErrorRecovery) {
+  EXPECT_EQ("(\xed\xa0\x80)", FX_UTF8Encode(L"(\xd800)")) << "High";
+  EXPECT_EQ("(\xed\xb0\x80)", FX_UTF8Encode(L"(\xdc00)")) << "Low";
+  EXPECT_EQ("(\xed\xa0\x80🎨)", FX_UTF8Encode(L"(\xd800\xd83c\xdfa8)"))
+      << "High-high";
+  EXPECT_EQ("(🎨\xed\xb0\x80)", FX_UTF8Encode(L"(\xd83c\xdfa8\xdc00)"))
+      << "Low-low";
+}
+#endif  // defined(WCHAR_T_IS_UTF16)
+
+TEST(fxstring, FXUTF8Decode) {
   EXPECT_EQ(L"", FX_UTF8Decode(ByteStringView()));
   EXPECT_EQ(
       L"x"
       L"\u0080"
       L"\u00ff"
+      L"\ud7ff"
+      L"\ue000"
       L"\uff2c"
+      L"\uffff"
       L"y",
       FX_UTF8Decode("x"
-                    "\xc2\x80"
-                    "\xc3\xbf"
-                    "\xef\xbc\xac"
+                    "\u0080"
+                    "\u00ff"
+                    "\ud7ff"
+                    "\ue000"
+                    "\uff2c"
+                    "\uffff"
                     "y"));
-  EXPECT_EQ(L"a(A) b() c() d() e().",
-            FX_UTF8Decode("a(\xc2\x41) "      // Invalid continuation.
-                          "b(\xc2\xc2) "      // Invalid continuation.
-                          "c(\xc2\xff\x80) "  // Invalid continuation.
-                          "d(\x80\x80) "      // Invalid leading.
-                          "e(\xff\x80\x80)"   // Invalid leading.
-                          "."));
 }
 
-TEST(fxstring, FX_UTF8EncodeDecodeConsistency) {
+TEST(fxstring, FXUTF8DecodeSupplementary) {
+  EXPECT_EQ(
+      L"\U00010000"
+      L"\U0001f3a8"
+      L"\U0010ffff",
+      FX_UTF8Decode("\U00010000"
+                    "🎨"
+                    "\U0010ffff"));
+}
+
+TEST(fxstring, FXUTF8DecodeErrorRecovery) {
+  EXPECT_EQ(L"(A)", FX_UTF8Decode("(\xc2\x41)")) << "Invalid continuation";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\xc2\xc2)")) << "Invalid continuation";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\xc2\xff\x80)")) << "Invalid continuation";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\x80\x80)")) << "Invalid leading";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\xff\x80\x80)")) << "Invalid leading";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\xf8\x80\x80\x80\x80)"))
+      << "Invalid leading";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\xf8\x88\x80\x80\x80)"))
+      << "Invalid leading";
+  EXPECT_EQ(L"()", FX_UTF8Decode("(\xf4\x90\x80\x80)"))
+      << "Code point greater than U+10FFFF";
+}
+
+TEST(fxstring, FXUTF8EncodeDecodeConsistency) {
   WideString wstr;
   wstr.Reserve(0x10000);
-  for (int w = 0; w < 0x10000; ++w)
+  for (char32_t w = 0; w < pdfium::kMinimumSupplementaryCodePoint; ++w) {
+    if (pdfium::IsHighSurrogate(w) || pdfium::IsLowSurrogate(w)) {
+      // Skip UTF-16 surrogates.
+      continue;
+    }
     wstr += static_cast<wchar_t>(w);
+  }
+  ASSERT_EQ(0xf800u, wstr.GetLength());
 
   ByteString bstr = FX_UTF8Encode(wstr.AsStringView());
   WideString wstr2 = FX_UTF8Decode(bstr.AsStringView());
-  EXPECT_EQ(0x10000u, wstr2.GetLength());
+  EXPECT_EQ(wstr, wstr2);
+}
+
+TEST(fxstring, FXUTF8EncodeDecodeConsistencyUnpairedHighSurrogates) {
+  WideString wstr;
+  wstr.Reserve(0x400);
+  for (wchar_t w = pdfium::kMinimumHighSurrogateCodeUnit;
+       w <= pdfium::kMaximumHighSurrogateCodeUnit; ++w) {
+    wstr += w;
+  }
+  ASSERT_EQ(0x400u, wstr.GetLength());
+
+  ByteString bstr = FX_UTF8Encode(wstr.AsStringView());
+  WideString wstr2 = FX_UTF8Decode(bstr.AsStringView());
+  EXPECT_EQ(wstr, wstr2);
+}
+
+TEST(fxstring, FXUTF8EncodeDecodeConsistencyUnpairedLowSurrogates) {
+  WideString wstr;
+  wstr.Reserve(0x400);
+  for (wchar_t w = pdfium::kMinimumLowSurrogateCodeUnit;
+       w <= pdfium::kMaximumLowSurrogateCodeUnit; ++w) {
+    wstr += w;
+  }
+  ASSERT_EQ(0x400u, wstr.GetLength());
+
+  ByteString bstr = FX_UTF8Encode(wstr.AsStringView());
+  WideString wstr2 = FX_UTF8Decode(bstr.AsStringView());
   EXPECT_EQ(wstr, wstr2);
 }
 
diff --git a/core/fxcrt/fx_system.h b/core/fxcrt/fx_system.h
index 52f9ce5..ca910f8 100644
--- a/core/fxcrt/fx_system.h
+++ b/core/fxcrt/fx_system.h
@@ -11,7 +11,6 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <wchar.h>
 
 #include "build/build_config.h"
@@ -92,31 +91,6 @@
 
 // C++-only section
 
-// Overloaded functions for C++ templates
-inline size_t FXSYS_len(const char* ptr) {
-  return strlen(ptr);
-}
-
-inline size_t FXSYS_len(const wchar_t* ptr) {
-  return wcslen(ptr);
-}
-
-inline int FXSYS_cmp(const char* ptr1, const char* ptr2, size_t len) {
-  return memcmp(ptr1, ptr2, len);
-}
-
-inline int FXSYS_cmp(const wchar_t* ptr1, const wchar_t* ptr2, size_t len) {
-  return wmemcmp(ptr1, ptr2, len);
-}
-
-inline const char* FXSYS_chr(const char* ptr, char ch, size_t len) {
-  return reinterpret_cast<const char*>(memchr(ptr, ch, len));
-}
-
-inline const wchar_t* FXSYS_chr(const wchar_t* ptr, wchar_t ch, size_t len) {
-  return wmemchr(ptr, ch, len);
-}
-
 // Could be C, but uses C++-style casting.
 #define FXSYS_UINT16_GET_LSBFIRST(p)                               \
   (static_cast<uint16_t>(                                          \
diff --git a/core/fxcrt/observed_ptr.h b/core/fxcrt/observed_ptr.h
index 7e55dc6..05b03d8 100644
--- a/core/fxcrt/observed_ptr.h
+++ b/core/fxcrt/observed_ptr.h
@@ -9,6 +9,7 @@
 
 #include <set>
 
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "third_party/base/check.h"
 
 namespace fxcrt {
@@ -91,7 +92,7 @@
   T* operator->() const { return m_pObservable; }
 
  private:
-  T* m_pObservable = nullptr;
+  UNOWNED_PTR_EXCLUSION T* m_pObservable = nullptr;
 };
 
 template <typename T, typename U>
diff --git a/core/fxcrt/pdfium_span_unittest.cpp b/core/fxcrt/pdfium_span_unittest.cpp
index d0dfee2..e9da064 100644
--- a/core/fxcrt/pdfium_span_unittest.cpp
+++ b/core/fxcrt/pdfium_span_unittest.cpp
@@ -5,7 +5,7 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 // Tests PDFium-modifications to base::span. The name of this file is
 // chosen to avoid collisions with base's span_unittest.cc
diff --git a/core/fxcrt/scoped_set_insertion.h b/core/fxcrt/scoped_set_insertion.h
index 3a0ff0e..d930982 100644
--- a/core/fxcrt/scoped_set_insertion.h
+++ b/core/fxcrt/scoped_set_insertion.h
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/check.h"
 
 namespace fxcrt {
@@ -29,7 +30,7 @@
   ~ScopedSetInsertion() { set_->erase(insert_results_.first); }
 
  private:
-  std::set<T>* const set_;
+  UnownedPtr<std::set<T>> const set_;
   const std::pair<typename std::set<T>::iterator, bool> insert_results_;
 };
 
diff --git a/core/fxcrt/shared_copy_on_write_unittest.cpp b/core/fxcrt/shared_copy_on_write_unittest.cpp
index 979058b..2e8c9e8 100644
--- a/core/fxcrt/shared_copy_on_write_unittest.cpp
+++ b/core/fxcrt/shared_copy_on_write_unittest.cpp
@@ -31,6 +31,11 @@
 
 class Object final : public Retainable {
  public:
+  CONSTRUCT_VIA_MAKE_RETAIN;
+
+  RetainPtr<Object> Clone() const { return pdfium::MakeRetain<Object>(*this); }
+
+ private:
   Object(Observer* observer, const std::string& name)
       : name_(name), observer_(observer) {
     observer->OnConstruct(name_);
@@ -40,9 +45,6 @@
   }
   ~Object() override { observer_->OnDestruct(name_); }
 
-  RetainPtr<Object> Clone() const { return pdfium::MakeRetain<Object>(*this); }
-
- private:
   std::string name_;
   Observer* observer_;
 };
diff --git a/core/fxcrt/span_util.h b/core/fxcrt/span_util.h
index c911617..2a34980 100644
--- a/core/fxcrt/span_util.h
+++ b/core/fxcrt/span_util.h
@@ -5,10 +5,9 @@
 #ifndef CORE_FXCRT_SPAN_UTIL_H_
 #define CORE_FXCRT_SPAN_UTIL_H_
 
-#include <string.h>
-
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -18,7 +17,7 @@
           typename = pdfium::internal::EnableIfLegalSpanConversion<T, U>>
 void spancpy(pdfium::span<T> dst, pdfium::span<U> src) {
   CHECK_GE(dst.size_bytes(), src.size_bytes());
-  memcpy(dst.data(), src.data(), src.size_bytes());
+  FXSYS_memcpy(dst.data(), src.data(), src.size_bytes());
 }
 
 // Bounds-checked moves from spans into spans.
@@ -27,19 +26,19 @@
           typename = pdfium::internal::EnableIfLegalSpanConversion<T, U>>
 void spanmove(pdfium::span<T> dst, pdfium::span<U> src) {
   CHECK_GE(dst.size_bytes(), src.size_bytes());
-  memmove(dst.data(), src.data(), src.size_bytes());
+  FXSYS_memmove(dst.data(), src.data(), src.size_bytes());
 }
 
 // Bounds-checked sets into spans.
 template <typename T>
 void spanset(pdfium::span<T> dst, uint8_t val) {
-  memset(dst.data(), val, dst.size_bytes());
+  FXSYS_memset(dst.data(), val, dst.size_bytes());
 }
 
 // Bounds-checked zeroing of spans.
 template <typename T>
 void spanclr(pdfium::span<T> dst) {
-  memset(dst.data(), 0, dst.size_bytes());
+  FXSYS_memset(dst.data(), 0, dst.size_bytes());
 }
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/string_data_template.cpp b/core/fxcrt/string_data_template.cpp
index 6e75007..a659cc2 100644
--- a/core/fxcrt/string_data_template.cpp
+++ b/core/fxcrt/string_data_template.cpp
@@ -10,6 +10,7 @@
 
 #include <new>
 
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "third_party/base/check.h"
@@ -21,7 +22,7 @@
 template <typename CharType>
 StringDataTemplate<CharType>* StringDataTemplate<CharType>::Create(
     size_t nLen) {
-  DCHECK_GT(nLen, 0);
+  DCHECK_GT(nLen, 0u);
 
   // Calculate space needed for the fixed portion of the struct plus the
   // NUL char that is not included in |m_nAllocLength|.
@@ -57,7 +58,7 @@
 template <typename CharType>
 void StringDataTemplate<CharType>::Release() {
   if (--m_nRefs <= 0)
-    FX_Free(this);
+    FX_StringFree(this);
 }
 
 template <typename CharType>
@@ -71,9 +72,9 @@
 template <typename CharType>
 void StringDataTemplate<CharType>::CopyContents(const CharType* pStr,
                                                 size_t nLen) {
-  DCHECK_GE(nLen, 0);
+  DCHECK_GE(nLen, 0u);
   DCHECK_LE(nLen, m_nAllocLength);
-  memcpy(m_String, pStr, nLen * sizeof(CharType));
+  FXSYS_memcpy(m_String, pStr, nLen * sizeof(CharType));
   m_String[nLen] = 0;
 }
 
@@ -81,10 +82,10 @@
 void StringDataTemplate<CharType>::CopyContentsAt(size_t offset,
                                                   const CharType* pStr,
                                                   size_t nLen) {
-  DCHECK_GE(offset, 0);
-  DCHECK_GE(nLen, 0);
+  DCHECK_GE(offset, 0u);
+  DCHECK_GE(nLen, 0u);
   DCHECK_LE(offset + nLen, m_nAllocLength);
-  memcpy(m_String + offset, pStr, nLen * sizeof(CharType));
+  FXSYS_memcpy(m_String + offset, pStr, nLen * sizeof(CharType));
   m_String[offset + nLen] = 0;
 }
 
@@ -92,7 +93,7 @@
 StringDataTemplate<CharType>::StringDataTemplate(size_t dataLen,
                                                  size_t allocLen)
     : m_nDataLength(dataLen), m_nAllocLength(allocLen) {
-  DCHECK_GE(dataLen, 0);
+  DCHECK_GE(dataLen, 0u);
   DCHECK_LE(dataLen, allocLen);
   m_String[dataLen] = 0;
 }
diff --git a/core/fxcrt/string_test_support.cpp b/core/fxcrt/string_test_support.cpp
new file mode 100644
index 0000000..cbb1220
--- /dev/null
+++ b/core/fxcrt/string_test_support.cpp
@@ -0,0 +1,20 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ostream>
+
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/widestring.h"
+
+namespace fxcrt {
+
+void PrintTo(const ByteString& str, std::ostream* os) {
+  *os << str;
+}
+
+void PrintTo(const WideString& str, std::ostream* os) {
+  *os << str;
+}
+
+}  // namespace fxcrt
diff --git a/core/fxcrt/string_view_template.h b/core/fxcrt/string_view_template.h
index 67cd3ff..475490f 100644
--- a/core/fxcrt/string_view_template.h
+++ b/core/fxcrt/string_view_template.h
@@ -13,9 +13,10 @@
 #include <iterator>
 #include <type_traits>
 
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_system.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -214,7 +215,7 @@
     if (!IsValidIndex(first + count - 1))
       return StringViewTemplate();
 
-    return StringViewTemplate(m_Span.data() + first, count);
+    return StringViewTemplate(m_Span.subspan(first, count));
   }
 
   StringViewTemplate First(size_t count) const {
diff --git a/core/fxcrt/tree_node.h b/core/fxcrt/tree_node.h
index d30b6a4..b86d793 100644
--- a/core/fxcrt/tree_node.h
+++ b/core/fxcrt/tree_node.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "third_party/base/check.h"
 
 namespace fxcrt {
@@ -182,11 +183,11 @@
  private:
   friend class TreeNodeBase<T>;
 
-  T* m_pParent = nullptr;       // Raw, intra-tree pointer.
-  T* m_pFirstChild = nullptr;   // Raw, intra-tree pointer.
-  T* m_pLastChild = nullptr;    // Raw, intra-tree pointer.
-  T* m_pNextSibling = nullptr;  // Raw, intra-tree pointer
-  T* m_pPrevSibling = nullptr;  // Raw, intra-tree pointer
+  UNOWNED_PTR_EXCLUSION T* m_pParent = nullptr;       // intra-tree pointer.
+  UNOWNED_PTR_EXCLUSION T* m_pFirstChild = nullptr;   // intra-tree pointer.
+  UNOWNED_PTR_EXCLUSION T* m_pLastChild = nullptr;    // intra-tree pointer.
+  UNOWNED_PTR_EXCLUSION T* m_pNextSibling = nullptr;  // intra-tree pointer.
+  UNOWNED_PTR_EXCLUSION T* m_pPrevSibling = nullptr;  // intra-tree pointer.
 };
 
 }  // namespace fxcrt
diff --git a/core/fxcrt/unowned_ptr.h b/core/fxcrt/unowned_ptr.h
index 1a2c9bb..fa78beb 100644
--- a/core/fxcrt/unowned_ptr.h
+++ b/core/fxcrt/unowned_ptr.h
@@ -5,25 +5,24 @@
 #ifndef CORE_FXCRT_UNOWNED_PTR_H_
 #define CORE_FXCRT_UNOWNED_PTR_H_
 
-#include <cstddef>
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-#include "third_party/base/compiler_specific.h"
-
 // UnownedPtr is a smart pointer class that behaves very much like a
-// standard C-style pointer. The advantages of using it over raw
+// standard C-style pointer. The advantages of using it over native T*
 // pointers are:
 //
 // 1. It documents the nature of the pointer with no need to add a comment
-//    explaining that is it // Not owned. Additionally, an attempt to delete
-//    an unowned ptr will fail to compile rather than silently succeeding,
-//    since it is a class and not a raw pointer.
+//    explaining that is it // Not owned.
 //
-// 2. When built using the memory tool ASAN, the class provides a destructor
+// 2. An attempt to delete an unowned ptr will fail to compile rather
+//    than silently succeeding, since it is a class and not a raw pointer.
+//
+// 3. When built using the memory tool ASAN, the class provides a destructor
 //    which checks that the object being pointed to is still alive.
 //
+// 4. When built against PartitionAlloc's BRP feature, it provides the same
+//    UaF protections as base::raw_ptr<T>
+//
+// 5. It is initialized to nullptr by default.
+//
 // Hence, when using UnownedPtr, no dangling pointers are ever permitted,
 // even if they are not de-referenced after becoming dangling. The style of
 // programming required is that the lifetime an object containing an
@@ -37,6 +36,42 @@
 // other heap object. Use pdfium::span<> for the cases where indexing
 // into an unowned array is desired, which performs the same checks.
 
+#include "build/build_config.h"
+
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
+
+// Can only use base::raw_ptr<> impls that force nullptr initialization.
+#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || BUILDFLAG(USE_ASAN_UNOWNED_PTR)
+#define UNOWNED_PTR_IS_BASE_RAW_PTR
+#endif
+
+#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) || BUILDFLAG(USE_ASAN_UNOWNED_PTR)
+#define UNOWNED_PTR_DANGLING_CHECKS
+#endif
+#endif  // PDF_USE_PARTITION_ALLOC
+
+#if defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
+#include "base/allocator/partition_allocator/pointers/raw_ptr.h"
+
+template <typename T>
+using UnownedPtr = raw_ptr<T>;
+
+#else  // UNOWNED_PTR_IS_BASE_RAW_PTR
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#include "third_party/base/compiler_specific.h"
+
+#if defined(ADDRESS_SANITIZER)
+#include <cstdint>
+#define UNOWNED_PTR_DANGLING_CHECKS
+#endif
+
 namespace pdfium {
 
 template <typename T>
@@ -177,13 +212,15 @@
 #endif
   }
 
-  T* m_pObj = nullptr;
+  UNOWNED_PTR_EXCLUSION T* m_pObj = nullptr;
 };
 
 }  // namespace fxcrt
 
 using fxcrt::UnownedPtr;
 
+#endif  // defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
+
 namespace pdfium {
 
 // Type-deducing wrapper to make an UnownedPtr from an ordinary pointer,
diff --git a/core/fxcrt/unowned_ptr_exclusion.h b/core/fxcrt/unowned_ptr_exclusion.h
new file mode 100644
index 0000000..0594965
--- /dev/null
+++ b/core/fxcrt/unowned_ptr_exclusion.h
@@ -0,0 +1,17 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_UNOWNED_PTR_EXCLUSION_H_
+#define CORE_FXCRT_UNOWNED_PTR_EXCLUSION_H_
+
+#include "build/build_config.h"
+
+#if defined(PDF_ENABLE_UNOWNED_PTR_EXCLUSION)
+// TODO(tsepez): convert to PA copy of this code.
+#define UNOWNED_PTR_EXCLUSION __attribute__((annotate("raw_ptr_exclusion")))
+#else
+#define UNOWNED_PTR_EXCLUSION
+#endif
+
+#endif  // CORE_FXCRT_UNOWNED_PTR_EXCLUSION_H_
diff --git a/core/fxcrt/unowned_ptr_unittest.cpp b/core/fxcrt/unowned_ptr_unittest.cpp
index f0c10ee..8c55108 100644
--- a/core/fxcrt/unowned_ptr_unittest.cpp
+++ b/core/fxcrt/unowned_ptr_unittest.cpp
@@ -4,6 +4,7 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 
+#include <atomic>
 #include <functional>
 #include <memory>
 #include <set>
@@ -12,6 +13,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/containers/contains.h"
 
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
+#endif
+
 namespace fxcrt {
 namespace {
 
@@ -162,7 +167,7 @@
 }
 
 TEST(UnownedPtr, PtrNotOk) {
-#if defined(ADDRESS_SANITIZER)
+#if defined(UNOWNED_PTR_DANGLING_CHECKS)
   EXPECT_DEATH(DeleteDangling(), "");
 #else
   DeleteDangling();
@@ -179,7 +184,7 @@
 }
 
 TEST(UnownedPtr, AssignNotOk) {
-#if defined(ADDRESS_SANITIZER)
+#if defined(UNOWNED_PTR_DANGLING_CHECKS)
   EXPECT_DEATH(AssignDangling(), "");
 #else
   AssignDangling();
@@ -196,7 +201,7 @@
 }
 
 TEST(UnownedPtr, ReleaseNotOk) {
-#if defined(ADDRESS_SANITIZER)
+#if defined(UNOWNED_PTR_DANGLING_CHECKS)
   EXPECT_DEATH(ReleaseDangling(), "");
 #else
   ReleaseDangling();
@@ -259,4 +264,34 @@
   EXPECT_FALSE(pdfium::Contains(holder, &foos[1]));
 }
 
+#if defined(PDF_USE_PARTITION_ALLOC)
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+
+TEST(UnownedPtr, DanglingGetsQuarantined) {
+  partition_alloc::PartitionRoot* root =
+      allocator_shim::internal::PartitionAllocMalloc::Allocator();
+  size_t original_byte_count =
+      root->total_size_of_brp_quarantined_bytes.load(std::memory_order_relaxed);
+
+  auto ptr = std::make_unique<double>(4.0);
+  UnownedPtr<double> dangler = ptr.get();
+  EXPECT_EQ(
+      root->total_size_of_brp_quarantined_bytes.load(std::memory_order_relaxed),
+      original_byte_count);
+
+  ptr.reset();
+  EXPECT_GE(
+      root->total_size_of_brp_quarantined_bytes.load(std::memory_order_relaxed),
+      original_byte_count + sizeof(double));
+
+  dangler = nullptr;
+  EXPECT_EQ(
+      root->total_size_of_brp_quarantined_bytes.load(std::memory_order_relaxed),
+      original_byte_count);
+}
+
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) ...
+#endif  // PDF_USE_PARTITION_ALLOC
+
 }  // namespace fxcrt
diff --git a/core/fxcrt/utf16.h b/core/fxcrt/utf16.h
new file mode 100644
index 0000000..f42f190
--- /dev/null
+++ b/core/fxcrt/utf16.h
@@ -0,0 +1,107 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_UTF16_H_
+#define CORE_FXCRT_UTF16_H_
+
+#include "third_party/base/check.h"
+
+namespace pdfium {
+
+// The number of suffix bits in a UTF-16 surrogate.
+inline constexpr int kSurrogateBits = 10;
+
+// A bitmask for the suffix of a UTF-16 surrogate.
+inline constexpr char16_t kSurrogateMask = (1 << kSurrogateBits) - 1;
+
+// The first supplementary code point, `U+10000`.
+inline constexpr char32_t kMinimumSupplementaryCodePoint = 0x10000;
+
+// The last supplementary code point, `U+10FFFF`.
+inline constexpr char32_t kMaximumSupplementaryCodePoint =
+    kMinimumSupplementaryCodePoint +
+    (kSurrogateMask << kSurrogateBits | kSurrogateMask);
+
+// The first UTF-16 high surrogate code unit, `U+D800`.
+inline constexpr char16_t kMinimumHighSurrogateCodeUnit = 0xd800;
+
+// The last UTF-16 high surrogate code unit, `U+DBFF`.
+inline constexpr char16_t kMaximumHighSurrogateCodeUnit =
+    kMinimumHighSurrogateCodeUnit | kSurrogateMask;
+
+// The first UTF-16 low surrogate code unit, `U+DC00`.
+inline constexpr char16_t kMinimumLowSurrogateCodeUnit =
+    kMaximumHighSurrogateCodeUnit + 1;
+
+// The last UTF-16 low surrogate code unit, `U+DFFF`.
+inline constexpr char16_t kMaximumLowSurrogateCodeUnit =
+    kMinimumLowSurrogateCodeUnit | kSurrogateMask;
+
+// Returns `true` if `code_point` is in a supplementary plane, and therefore
+// requires encoding as a UTF-16 surrogate pair.
+constexpr bool IsSupplementary(char32_t code_point) {
+  return code_point >= kMinimumSupplementaryCodePoint &&
+         code_point <= kMaximumSupplementaryCodePoint;
+}
+
+// Returns `true` if `code_point` is a UTF-16 high surrogate.
+constexpr bool IsHighSurrogate(char32_t code_point) {
+  return code_point >= kMinimumHighSurrogateCodeUnit &&
+         code_point <= kMaximumHighSurrogateCodeUnit;
+}
+
+// Returns `true` if `code_point` is a UTF-16 low surrogate.
+constexpr bool IsLowSurrogate(char32_t code_point) {
+  return code_point >= kMinimumLowSurrogateCodeUnit &&
+         code_point <= kMaximumLowSurrogateCodeUnit;
+}
+
+// A UTF-16 surrogate pair.
+class SurrogatePair final {
+ public:
+  // Constructs a surrogate pair from a high and a low surrogate.
+  constexpr SurrogatePair(char16_t high, char16_t low)
+      : high_(high), low_(low) {
+    DCHECK(IsHighSurrogate(high_));
+    DCHECK(IsLowSurrogate(low_));
+  }
+
+  // Constructs a surrogate pair from a code point.
+  explicit constexpr SurrogatePair(char32_t code_point)
+      : high_(GetHighSurrogate(code_point)), low_(GetLowSurrogate(code_point)) {
+    // This constructor initializes `high_` and `low_` using helper functions
+    // because C++17 requires it for `constexpr` constructors.
+    DCHECK(IsSupplementary(code_point));
+  }
+
+  constexpr char16_t high() const { return high_; }
+  constexpr char16_t low() const { return low_; }
+
+  // Decodes this surrogate pair to a code point.
+  constexpr char32_t ToCodePoint() const {
+    char32_t code_point = low_ & kSurrogateMask;
+    code_point |= (high_ & kSurrogateMask) << kSurrogateBits;
+    return kMinimumSupplementaryCodePoint + code_point;
+  }
+
+ private:
+  static constexpr char16_t GetHighSurrogate(char32_t code_point) {
+    code_point -= kMinimumSupplementaryCodePoint;
+    char16_t code_unit = (code_point >> kSurrogateBits) & kSurrogateMask;
+    return kMinimumHighSurrogateCodeUnit | code_unit;
+  }
+
+  static constexpr char16_t GetLowSurrogate(char32_t code_point) {
+    code_point -= kMinimumSupplementaryCodePoint;
+    char16_t code_unit = code_point & kSurrogateMask;
+    return kMinimumLowSurrogateCodeUnit | code_unit;
+  }
+
+  char16_t high_;
+  char16_t low_;
+};
+
+}  // namespace pdfium
+
+#endif  // CORE_FXCRT_UTF16_H_
diff --git a/core/fxcrt/utf16_unittest.cpp b/core/fxcrt/utf16_unittest.cpp
new file mode 100644
index 0000000..bab1bdd
--- /dev/null
+++ b/core/fxcrt/utf16_unittest.cpp
@@ -0,0 +1,61 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/utf16.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace pdfium {
+
+static_assert(kSurrogateMask == 0x3ff);
+static_assert(kMaximumSupplementaryCodePoint == 0x10ffff);
+static_assert(kMaximumHighSurrogateCodeUnit == 0xdbff);
+static_assert(kMinimumLowSurrogateCodeUnit == 0xdc00);
+static_assert(kMaximumLowSurrogateCodeUnit == 0xdfff);
+
+static_assert(!IsSupplementary(0xffff));
+static_assert(IsSupplementary(0x10000));
+static_assert(IsSupplementary(0x10ffff));
+static_assert(!IsSupplementary(0x110000));
+
+static_assert(!IsHighSurrogate(0xd7ff));
+static_assert(IsHighSurrogate(0xd800));
+static_assert(IsHighSurrogate(0xdbff));
+static_assert(!IsHighSurrogate(0xdc00));
+
+static_assert(!IsLowSurrogate(0xdbff));
+static_assert(IsLowSurrogate(0xdc00));
+static_assert(IsLowSurrogate(0xdfff));
+static_assert(!IsLowSurrogate(0xe000));
+
+static_assert(SurrogatePair(0xd800, 0xdc00).high() == 0xd800);
+static_assert(SurrogatePair(0xd800, 0xdc00).low() == 0xdc00);
+static_assert(SurrogatePair(0xd800, 0xdc00).ToCodePoint() == 0x10000);
+
+static_assert(SurrogatePair(0xdbff, 0xdfff).high() == 0xdbff);
+static_assert(SurrogatePair(0xdbff, 0xdfff).low() == 0xdfff);
+static_assert(SurrogatePair(0xdbff, 0xdfff).ToCodePoint() == 0x10ffff);
+
+static_assert(SurrogatePair(0x10000).high() == 0xd800);
+static_assert(SurrogatePair(0x10000).low() == 0xdc00);
+static_assert(SurrogatePair(0x10000).ToCodePoint() == 0x10000);
+
+static_assert(SurrogatePair(0x10ffff).high() == 0xdbff);
+static_assert(SurrogatePair(0x10ffff).low() == 0xdfff);
+static_assert(SurrogatePair(0x10ffff).ToCodePoint() == 0x10ffff);
+
+TEST(SurrogatePairTest, RoundTrip) {
+  for (char32_t code_point = kMinimumSupplementaryCodePoint;
+       code_point <= kMaximumSupplementaryCodePoint; ++code_point) {
+    SurrogatePair from_code_point(code_point);
+    EXPECT_EQ(code_point, from_code_point.ToCodePoint());
+
+    SurrogatePair from_pair(from_code_point.high(), from_code_point.low());
+    EXPECT_EQ(from_code_point.high(), from_pair.high());
+    EXPECT_EQ(from_code_point.low(), from_pair.low());
+    EXPECT_EQ(code_point, from_pair.ToCodePoint());
+  }
+}
+
+}  // namespace pdfium
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index f87447d..241e544 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -14,12 +14,12 @@
 
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/numerics/safe_math.h"
 
 template class fxcrt::StringDataTemplate<wchar_t>;
@@ -465,7 +465,7 @@
     return m_pData->m_nDataLength == 0;
 
   return wcslen(ptr) == m_pData->m_nDataLength &&
-         wmemcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
+         FXSYS_wmemcmp(ptr, m_pData->m_String, m_pData->m_nDataLength) == 0;
 }
 
 bool WideString::operator==(WideStringView str) const {
@@ -473,8 +473,8 @@
     return str.IsEmpty();
 
   return m_pData->m_nDataLength == str.GetLength() &&
-         wmemcmp(m_pData->m_String, str.unterminated_c_str(),
-                 str.GetLength()) == 0;
+         FXSYS_wmemcmp(m_pData->m_String, str.unterminated_c_str(),
+                       str.GetLength()) == 0;
 }
 
 bool WideString::operator==(const WideString& other) const {
@@ -504,8 +504,8 @@
 
   size_t len = GetLength();
   size_t other_len = str.GetLength();
-  int result =
-      wmemcmp(c_str(), str.unterminated_c_str(), std::min(len, other_len));
+  int result = FXSYS_wmemcmp(c_str(), str.unterminated_c_str(),
+                             std::min(len, other_len));
   return result < 0 || (result == 0 && len < other_len);
 }
 
@@ -607,8 +607,9 @@
     return 0;
 
   size_t old_length = m_pData->m_nDataLength;
-  if (count == 0 || index != pdfium::clamp<size_t>(index, 0, old_length))
+  if (count == 0 || index != std::clamp<size_t>(index, 0, old_length)) {
     return old_length;
+  }
 
   size_t removal_length = index + count;
   if (removal_length > old_length)
@@ -769,8 +770,8 @@
 
   const size_t new_length = cur_length + 1;
   ReallocBeforeWrite(new_length);
-  wmemmove(m_pData->m_String + index + 1, m_pData->m_String + index,
-           new_length - index);
+  FXSYS_wmemmove(m_pData->m_String + index + 1, m_pData->m_String + index,
+                 new_length - index);
   m_pData->m_String[index] = ch;
   m_pData->m_nDataLength = new_length;
   return new_length;
@@ -783,8 +784,8 @@
   if (!IsValidIndex(start))
     return absl::nullopt;
 
-  const wchar_t* pStr =
-      wmemchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start);
+  const wchar_t* pStr = FXSYS_wmemchr(m_pData->m_String + start, ch,
+                                      m_pData->m_nDataLength - start);
   return pStr ? absl::optional<size_t>(
                     static_cast<size_t>(pStr - m_pData->m_String))
               : absl::nullopt;
@@ -905,13 +906,13 @@
     const wchar_t* pTarget =
         FX_wcsstr(pStart, static_cast<size_t>(pEnd - pStart),
                   pOld.unterminated_c_str(), nSourceLen);
-    wmemcpy(pDest, pStart, pTarget - pStart);
+    FXSYS_wmemcpy(pDest, pStart, pTarget - pStart);
     pDest += pTarget - pStart;
-    wmemcpy(pDest, pNew.unterminated_c_str(), pNew.GetLength());
+    FXSYS_wmemcpy(pDest, pNew.unterminated_c_str(), pNew.GetLength());
     pDest += pNew.GetLength();
     pStart = pTarget + nSourceLen;
   }
-  wmemcpy(pDest, pStart, pEnd - pStart);
+  FXSYS_wmemcpy(pDest, pStart, pEnd - pStart);
   m_pData.Swap(pNewData);
   return count;
 }
@@ -1010,7 +1011,7 @@
   size_t this_len = m_pData->m_nDataLength;
   size_t that_len = str.m_pData->m_nDataLength;
   size_t min_len = std::min(this_len, that_len);
-  int result = wmemcmp(m_pData->m_String, str.m_pData->m_String, min_len);
+  int result = FXSYS_wmemcmp(m_pData->m_String, str.m_pData->m_String, min_len);
   if (result != 0)
     return result;
   if (this_len == that_len)
diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h
index 24547dd..a01e960 100644
--- a/core/fxcrt/widestring.h
+++ b/core/fxcrt/widestring.h
@@ -22,7 +22,7 @@
 #include "core/fxcrt/string_view_template.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
@@ -32,6 +32,7 @@
 // avoids the cost of std::string's iterator stability guarantees.
 class WideString {
  public:
+  // TODO(crbug.com/pdfium/2031): Consider switching to `char16_t` instead.
   using CharType = wchar_t;
   using const_iterator = const CharType*;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
@@ -303,6 +304,13 @@
 std::wostream& operator<<(std::wostream& os, WideStringView str);
 std::ostream& operator<<(std::ostream& os, WideStringView str);
 
+// This is declared here for use in gtest-based tests but is defined in a test
+// support target. This should not be used in production code. Just use
+// operator<< from above instead.
+// In some cases, gtest will automatically use operator<< as well, but in this
+// case, it needs PrintTo() because WideString looks like a container to gtest.
+void PrintTo(const WideString& str, std::ostream* os);
+
 }  // namespace fxcrt
 
 using WideString = fxcrt::WideString;
diff --git a/core/fxcrt/widestring_unittest.cpp b/core/fxcrt/widestring_unittest.cpp
index d45c4e8..947926c 100644
--- a/core/fxcrt/widestring_unittest.cpp
+++ b/core/fxcrt/widestring_unittest.cpp
@@ -12,7 +12,7 @@
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
diff --git a/core/fxcrt/widetext_buffer.h b/core/fxcrt/widetext_buffer.h
index a02d128..5c29a0b 100644
--- a/core/fxcrt/widetext_buffer.h
+++ b/core/fxcrt/widetext_buffer.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/binary_buffer.h"
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace fxcrt {
 
diff --git a/core/fxcrt/xml/cfx_xmlparser.cpp b/core/fxcrt/xml/cfx_xmlparser.cpp
index a6f371f..6ca9ea4 100644
--- a/core/fxcrt/xml/cfx_xmlparser.cpp
+++ b/core/fxcrt/xml/cfx_xmlparser.cpp
@@ -13,6 +13,7 @@
 #include <stack>
 #include <utility>
 
+#include "core/fxcrt/autorestorer.h"
 #include "core/fxcrt/cfx_seekablestreamproxy.h"
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_codepage.h"
@@ -84,8 +85,8 @@
 
 std::unique_ptr<CFX_XMLDocument> CFX_XMLParser::Parse() {
   auto doc = std::make_unique<CFX_XMLDocument>();
+  AutoRestorer<UnownedPtr<CFX_XMLNode>> restorer(&current_node_);
   current_node_ = doc->GetRoot();
-
   return DoSyntaxParse(doc.get()) ? std::move(doc) : nullptr;
 }
 
@@ -458,8 +459,6 @@
             current_buffer_idx++;
           }
           break;
-        default:
-          break;
       }
     }
   }
diff --git a/core/fxcrt/xml/cfx_xmlparser.h b/core/fxcrt/xml/cfx_xmlparser.h
index 7a30b7c..268c9f2 100644
--- a/core/fxcrt/xml/cfx_xmlparser.h
+++ b/core/fxcrt/xml/cfx_xmlparser.h
@@ -11,6 +11,7 @@
 
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxcrt/widestring.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -53,7 +54,7 @@
   void ProcessTextChar(wchar_t ch);
   void ProcessTargetData();
 
-  CFX_XMLNode* current_node_ = nullptr;
+  UnownedPtr<CFX_XMLNode> current_node_;
   RetainPtr<CFX_SeekableStreamProxy> stream_;
   DataVector<wchar_t> current_text_;
   size_t xml_plane_size_ = 1024;
diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn
index c526c06..a3d2b0e 100644
--- a/core/fxge/BUILD.gn
+++ b/core/fxge/BUILD.gn
@@ -134,7 +134,10 @@
   }
 
   if (pdf_use_skia) {
-    sources += [ "skia/fx_skia_device.cpp" ]
+    sources += [
+      "skia/cfx_dibbase_skia.cpp",
+      "skia/fx_skia_device.cpp",
+    ]
     public_deps += [ "//skia" ]
   }
 
@@ -173,8 +176,6 @@
     sources += [
       "cfx_windowsrenderdevice.cpp",
       "cfx_windowsrenderdevice.h",
-      "dib/cfx_dibextractor.cpp",
-      "dib/cfx_dibextractor.h",
       "win32/cfx_psfonttracker.cpp",
       "win32/cfx_psfonttracker.h",
       "win32/cfx_psrenderer.cpp",
@@ -236,6 +237,8 @@
     deps += [
       ":fxge",
       "../../fpdfsdk",
+      "../fpdfapi/page",
+      "../fpdfapi/render",
       "//skia",
     ]
   }
diff --git a/core/fxge/DEPS b/core/fxge/DEPS
index 6492756..9094236 100644
--- a/core/fxge/DEPS
+++ b/core/fxge/DEPS
@@ -1,3 +1,5 @@
-include_rules = [
-  '+third_party/skia/include'
-]
+specific_include_rules = {
+  'cfx_dibbase\.h|cfx_glyphcache\.(cpp|h)|cfx_renderdevice\.cpp': [
+    '+third_party/skia/include',
+  ],
+}
diff --git a/core/fxge/agg/fx_agg_driver.cpp b/core/fxge/agg/fx_agg_driver.cpp
index a1949de..e1fd404 100644
--- a/core/fxge/agg/fx_agg_driver.cpp
+++ b/core/fxge/agg/fx_agg_driver.cpp
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxge/cfx_cliprgn.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_graphstatedata.h"
@@ -24,9 +25,8 @@
 #include "core/fxge/dib/cfx_imagestretcher.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
 
 // Ignore fallthrough warnings in agg23 headers.
 #if defined(__clang__)
@@ -52,8 +52,8 @@
 const float kMaxPos = 32000.0f;
 
 CFX_PointF HardClip(const CFX_PointF& pos) {
-  return CFX_PointF(pdfium::clamp(pos.x, -kMaxPos, kMaxPos),
-                    pdfium::clamp(pos.y, -kMaxPos, kMaxPos));
+  return CFX_PointF(std::clamp(pos.x, -kMaxPos, kMaxPos),
+                    std::clamp(pos.y, -kMaxPos, kMaxPos));
 }
 
 void RgbByteOrderCompositeRect(const RetainPtr<CFX_DIBitmap>& pBitmap,
@@ -74,7 +74,7 @@
   int src_b = FXARGB_B(argb);
   int Bpp = pBitmap->GetBPP() / 8;
   int dib_argb = FXARGB_TOBGRORDERDIB(argb);
-  pdfium::span<uint8_t> pBuffer = pBitmap->GetBuffer();
+  pdfium::span<uint8_t> pBuffer = pBitmap->GetWritableBuffer();
   if (src_alpha == 255) {
     for (int row = rect.top; row < rect.bottom; row++) {
       uint8_t* dest_scan =
@@ -151,8 +151,9 @@
   const size_t dest_x_offset = Fx2DSizeOrDie(dest_left, Bpp);
   const size_t dest_y_offset = Fx2DSizeOrDie(dest_top, dest_pitch);
 
-  pdfium::span<uint8_t> dest_span =
-      pBitmap->GetBuffer().subspan(dest_y_offset).subspan(dest_x_offset);
+  pdfium::span<uint8_t> dest_span = pBitmap->GetWritableBuffer()
+                                        .subspan(dest_y_offset)
+                                        .subspan(dest_x_offset);
   if (dest_format == src_format) {
     const size_t src_x_offset = Fx2DSizeOrDie(src_left, Bpp);
     for (int row = 0; row < height; row++) {
@@ -340,55 +341,61 @@
   void render(const Scanline& sl);
 
  private:
-  using CompositeSpanFunc = void (
-      CFX_Renderer::*)(uint8_t*, int, int, int, uint8_t*, int, int, uint8_t*);
+  using CompositeSpanFunc = void (CFX_Renderer::*)(uint8_t*,
+                                                   int,
+                                                   int,
+                                                   int,
+                                                   const uint8_t*,
+                                                   int,
+                                                   int,
+                                                   const uint8_t*);
 
   void CompositeSpan(uint8_t* dest_scan,
-                     uint8_t* backdrop_scan,
+                     const uint8_t* backdrop_scan,
                      int Bpp,
                      bool bDestAlpha,
                      int span_left,
                      int span_len,
-                     uint8_t* cover_scan,
+                     const uint8_t* cover_scan,
                      int clip_left,
                      int clip_right,
-                     uint8_t* clip_scan);
+                     const uint8_t* clip_scan);
 
   void CompositeSpan1bpp(uint8_t* dest_scan,
                          int Bpp,
                          int span_left,
                          int span_len,
-                         uint8_t* cover_scan,
+                         const uint8_t* cover_scan,
                          int clip_left,
                          int clip_right,
-                         uint8_t* clip_scan);
+                         const uint8_t* clip_scan);
 
   void CompositeSpanGray(uint8_t* dest_scan,
                          int Bpp,
                          int span_left,
                          int span_len,
-                         uint8_t* cover_scan,
+                         const uint8_t* cover_scan,
                          int clip_left,
                          int clip_right,
-                         uint8_t* clip_scan);
+                         const uint8_t* clip_scan);
 
   void CompositeSpanARGB(uint8_t* dest_scan,
                          int Bpp,
                          int span_left,
                          int span_len,
-                         uint8_t* cover_scan,
+                         const uint8_t* cover_scan,
                          int clip_left,
                          int clip_right,
-                         uint8_t* clip_scan);
+                         const uint8_t* clip_scan);
 
   void CompositeSpanRGB(uint8_t* dest_scan,
                         int Bpp,
                         int span_left,
                         int span_len,
-                        uint8_t* cover_scan,
+                        const uint8_t* cover_scan,
                         int clip_left,
                         int clip_right,
-                        uint8_t* clip_scan);
+                        const uint8_t* clip_scan);
 
   void CompositeSpan1bppHelper(uint8_t* dest_scan,
                                int col_start,
@@ -445,15 +452,15 @@
 };
 
 void CFX_Renderer::CompositeSpan(uint8_t* dest_scan,
-                                 uint8_t* backdrop_scan,
+                                 const uint8_t* backdrop_scan,
                                  int Bpp,
                                  bool bDestAlpha,
                                  int span_left,
                                  int span_len,
-                                 uint8_t* cover_scan,
+                                 const uint8_t* cover_scan,
                                  int clip_left,
                                  int clip_right,
-                                 uint8_t* clip_scan) {
+                                 const uint8_t* clip_scan) {
   int col_start = GetColStart(span_left, clip_left);
   int col_end = GetColEnd(span_left, span_len, clip_right);
   if (Bpp) {
@@ -592,10 +599,10 @@
                                      int Bpp,
                                      int span_left,
                                      int span_len,
-                                     uint8_t* cover_scan,
+                                     const uint8_t* cover_scan,
                                      int clip_left,
                                      int clip_right,
-                                     uint8_t* clip_scan) {
+                                     const uint8_t* clip_scan) {
   DCHECK(!m_bRgbByteOrder);
   int col_start = GetColStart(span_left, clip_left);
   int col_end = GetColEnd(span_left, span_len, clip_right);
@@ -608,10 +615,10 @@
                                      int Bpp,
                                      int span_left,
                                      int span_len,
-                                     uint8_t* cover_scan,
+                                     const uint8_t* cover_scan,
                                      int clip_left,
                                      int clip_right,
-                                     uint8_t* clip_scan) {
+                                     const uint8_t* clip_scan) {
   DCHECK(!m_bRgbByteOrder);
   int col_start = GetColStart(span_left, clip_left);
   int col_end = GetColEnd(span_left, span_len, clip_right);
@@ -632,10 +639,10 @@
                                      int Bpp,
                                      int span_left,
                                      int span_len,
-                                     uint8_t* cover_scan,
+                                     const uint8_t* cover_scan,
                                      int clip_left,
                                      int clip_right,
-                                     uint8_t* clip_scan) {
+                                     const uint8_t* clip_scan) {
   int col_start = GetColStart(span_left, clip_left);
   int col_end = GetColEnd(span_left, span_len, clip_right);
   dest_scan += col_start * Bpp;
@@ -700,10 +707,10 @@
                                     int Bpp,
                                     int span_left,
                                     int span_len,
-                                    uint8_t* cover_scan,
+                                    const uint8_t* cover_scan,
                                     int clip_left,
                                     int clip_right,
-                                    uint8_t* clip_scan) {
+                                    const uint8_t* clip_scan) {
   int col_start = GetColStart(span_left, clip_left);
   int col_end = GetColEnd(span_left, span_len, clip_right);
   dest_scan += col_start * Bpp;
@@ -796,8 +803,8 @@
     return;
 
   uint8_t* dest_scan =
-      m_pDevice->GetBuffer().subspan(m_pDevice->GetPitch() * y).data();
-  uint8_t* backdrop_scan = nullptr;
+      m_pDevice->GetWritableBuffer().subspan(m_pDevice->GetPitch() * y).data();
+  const uint8_t* backdrop_scan = nullptr;
   if (m_pBackdropDevice) {
     backdrop_scan = m_pBackdropDevice->GetBuffer()
                         .subspan(m_pBackdropDevice->GetPitch() * y)
@@ -813,7 +820,7 @@
 
     int x = span->x;
     uint8_t* dest_pos = nullptr;
-    uint8_t* backdrop_pos = nullptr;
+    const uint8_t* backdrop_pos = nullptr;
     if (Bpp) {
       backdrop_pos = backdrop_scan ? backdrop_scan + x * Bpp : nullptr;
       dest_pos = dest_scan + x * Bpp;
@@ -821,7 +828,7 @@
       dest_pos = dest_scan + x / 8;
       backdrop_pos = backdrop_scan ? backdrop_scan + x / 8 : nullptr;
     }
-    uint8_t* clip_pos = nullptr;
+    const uint8_t* clip_pos = nullptr;
     if (m_pClipMask) {
       // TODO(crbug.com/1382604): use subspan arithmetic.
       clip_pos = m_pClipMask->GetBuffer().data() +
@@ -902,7 +909,7 @@
   }
 
  private:
-  base_ren_type* m_ren;
+  UNOWNED_PTR_EXCLUSION base_ren_type* m_ren;
   color_type m_color;
   unsigned m_left;
   unsigned m_top;
@@ -1020,8 +1027,7 @@
       return flags;
     }
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -1054,7 +1060,7 @@
   auto pThisLayer = pdfium::MakeRetain<CFX_DIBitmap>();
   pThisLayer->Create(path_rect.Width(), path_rect.Height(),
                      FXDIB_Format::k8bppMask);
-  agg::rendering_buffer raw_buf(pThisLayer->GetBuffer().data(),
+  agg::rendering_buffer raw_buf(pThisLayer->GetWritableBuffer().data(),
                                 pThisLayer->GetWidth(), pThisLayer->GetHeight(),
                                 pThisLayer->GetPitch());
   agg::pixfmt_gray8 pixel_buf(raw_buf);
diff --git a/core/fxge/agg/fx_agg_driver.h b/core/fxge/agg/fx_agg_driver.h
index 3b3d8dc..523bb0c 100644
--- a/core/fxge/agg/fx_agg_driver.h
+++ b/core/fxge/agg/fx_agg_driver.h
@@ -15,6 +15,10 @@
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/renderdevicedriver_iface.h"
 
+#if BUILDFLAG(IS_APPLE)
+#include "core/fxcrt/unowned_ptr_exclusion.h"
+#endif
+
 class CFX_ClipRgn;
 class CFX_GraphStateData;
 class CFX_Matrix;
@@ -109,7 +113,7 @@
   std::unique_ptr<CFX_ClipRgn> m_pClipRgn;
   std::vector<std::unique_ptr<CFX_ClipRgn>> m_StateStack;
 #if BUILDFLAG(IS_APPLE)
-  void* m_pPlatformGraphics = nullptr;
+  UNOWNED_PTR_EXCLUSION void* m_pPlatformGraphics = nullptr;
 #endif
   CFX_FillRenderOptions m_FillOptions;
   const bool m_bRgbByteOrder;
diff --git a/core/fxge/android/cfpf_skiafont.h b/core/fxge/android/cfpf_skiafont.h
index 840db63..c320806 100644
--- a/core/fxge/android/cfpf_skiafont.h
+++ b/core/fxge/android/cfpf_skiafont.h
@@ -15,7 +15,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_face.h"
 #include "core/fxge/freetype/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFPF_SkiaFontMgr;
 class CFPF_SkiaPathFont;
diff --git a/core/fxge/android/cfx_androidfontinfo.h b/core/fxge/android/cfx_androidfontinfo.h
index 5f68899..54500ba 100644
--- a/core/fxge/android/cfx_androidfontinfo.h
+++ b/core/fxge/android/cfx_androidfontinfo.h
@@ -12,7 +12,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fontmapper.h"
 #include "core/fxge/systemfontinfo_iface.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFPF_SkiaFontMgr;
 
diff --git a/core/fxge/android/fx_android_impl.cpp b/core/fxge/android/fx_android_impl.cpp
index e06d611..12783f5 100644
--- a/core/fxge/android/fx_android_impl.cpp
+++ b/core/fxge/android/fx_android_impl.cpp
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/android/cfpf_skiadevicemodule.h"
 #include "core/fxge/android/cfx_androidfontinfo.h"
 #include "core/fxge/cfx_fontmgr.h"
@@ -33,7 +34,7 @@
   }
 
  private:
-  CFPF_SkiaDeviceModule* m_pDeviceModule = nullptr;
+  UnownedPtr<CFPF_SkiaDeviceModule> m_pDeviceModule;
 };
 
 // static
diff --git a/core/fxge/apple/fx_apple_impl.cpp b/core/fxge/apple/fx_apple_impl.cpp
index 54f694f..fa397c0 100644
--- a/core/fxge/apple/fx_apple_impl.cpp
+++ b/core/fxge/apple/fx_apple_impl.cpp
@@ -23,7 +23,7 @@
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/freetype/fx_freetype.h"
 #include "core/fxge/text_char_pos.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
diff --git a/core/fxge/apple/fx_quartz_device.cpp b/core/fxge/apple/fx_quartz_device.cpp
index 9f5f13f..24e0c9c 100644
--- a/core/fxge/apple/fx_quartz_device.cpp
+++ b/core/fxge/apple/fx_quartz_device.cpp
@@ -33,8 +33,8 @@
   }
   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   CGContextRef context = CGBitmapContextCreate(
-      pBitmap->GetBuffer().data(), pBitmap->GetWidth(), pBitmap->GetHeight(), 8,
-      pBitmap->GetPitch(), colorSpace, bmpInfo);
+      pBitmap->GetWritableBuffer().data(), pBitmap->GetWidth(),
+      pBitmap->GetHeight(), 8, pBitmap->GetPitch(), colorSpace, bmpInfo);
   CGColorSpaceRelease(colorSpace);
   return context;
 }
diff --git a/core/fxge/apple/fx_quartz_device.h b/core/fxge/apple/fx_quartz_device.h
index 0aaf520..97080e3 100644
--- a/core/fxge/apple/fx_quartz_device.h
+++ b/core/fxge/apple/fx_quartz_device.h
@@ -12,7 +12,7 @@
 
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBitmap;
 class CFX_Matrix;
diff --git a/core/fxge/cfx_color.cpp b/core/fxge/cfx_color.cpp
index 23cdd0f..35e03a5 100644
--- a/core/fxge/cfx_color.cpp
+++ b/core/fxge/cfx_color.cpp
@@ -87,8 +87,7 @@
         case CFX_Color::Type::kTransparent:
           break;
         case CFX_Color::Type::kGray:
-          NOTREACHED();
-          break;
+          NOTREACHED_NORETURN();
         case CFX_Color::Type::kRGB:
           ret = ConvertGRAY2RGB(fColor1);
           break;
@@ -105,8 +104,7 @@
           ret = ConvertRGB2GRAY(fColor1, fColor2, fColor3);
           break;
         case CFX_Color::Type::kRGB:
-          NOTREACHED();
-          break;
+          NOTREACHED_NORETURN();
         case CFX_Color::Type::kCMYK:
           ret = ConvertRGB2CMYK(fColor1, fColor2, fColor3);
           break;
@@ -123,8 +121,7 @@
           ret = ConvertCMYK2RGB(fColor1, fColor2, fColor3, fColor4);
           break;
         case CFX_Color::Type::kCMYK:
-          NOTREACHED();
-          break;
+          NOTREACHED_NORETURN();
       }
       break;
   }
diff --git a/core/fxge/cfx_defaultrenderdevice.h b/core/fxge/cfx_defaultrenderdevice.h
index f9c20fe..4d94b7a 100644
--- a/core/fxge/cfx_defaultrenderdevice.h
+++ b/core/fxge/cfx_defaultrenderdevice.h
@@ -13,8 +13,7 @@
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/dib/fx_dib.h"
 
-class SkPictureRecorder;
-struct SkRect;
+class SkCanvas;
 
 class CFX_DefaultRenderDevice final : public CFX_RenderDevice {
  public:
@@ -34,15 +33,8 @@
               RetainPtr<CFX_DIBitmap> pBackdropBitmap);
 
 #if defined(_SKIA_SUPPORT_)
-  bool AttachRecorder(SkPictureRecorder* recorder);
+  bool AttachCanvas(SkCanvas* canvas);
   void Clear(uint32_t color);
-  std::unique_ptr<SkPictureRecorder> CreateRecorder(const SkRect& bounds);
-  bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
-                       const RetainPtr<CFX_DIBBase>& pMask,
-                       int left,
-                       int top,
-                       int bitmap_alpha,
-                       BlendMode blend_type) override;
 #endif
 
   // Runtime check to see if Skia is the renderer variant in use.
diff --git a/core/fxge/cfx_face.h b/core/fxge/cfx_face.h
index 6f1ee5a..6c74eae 100644
--- a/core/fxge/cfx_face.h
+++ b/core/fxge/cfx_face.h
@@ -8,7 +8,7 @@
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/freetype/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_Face final : public Retainable, public Observable {
  public:
diff --git a/core/fxge/cfx_font.cpp b/core/fxge/cfx_font.cpp
index e9ca56b..0330386 100644
--- a/core/fxge/cfx_font.cpp
+++ b/core/fxge/cfx_font.cpp
@@ -28,8 +28,8 @@
 #include "core/fxge/fx_font.h"
 #include "core/fxge/scoped_font_transform.h"
 #include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 #define EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em)
 
@@ -45,30 +45,6 @@
   float m_CoordUnit;
 };
 
-// TODO(crbug.com/pdfium/1400): When FT_Done_MM_Var() is more likely to be
-// available to all users in the future, remove FreeMMVar() and use
-// FT_Done_MM_Var() directly.
-//
-// Use weak symbols to check if FT_Done_MM_Var() is available at runtime.
-#if !BUILDFLAG(IS_WIN)
-extern "C" __attribute__((weak)) decltype(FT_Done_MM_Var) FT_Done_MM_Var;
-#endif
-
-void FreeMMVar(FXFT_FaceRec* rec, FXFT_MM_VarPtr variation_desc) {
-#if BUILDFLAG(IS_WIN)
-  // Assume `use_system_freetype` GN var is never set on Windows.
-  constexpr bool has_ft_done_mm_var_func = true;
-#else
-  static const bool has_ft_done_mm_var_func = !!FT_Done_MM_Var;
-#endif
-  if (has_ft_done_mm_var_func) {
-    FT_Done_MM_Var(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
-                   variation_desc);
-  } else {
-    FXFT_Free(rec, variation_desc);
-  }
-}
-
 FX_RECT FXRectFromFTPos(FT_Pos left, FT_Pos top, FT_Pos right, FT_Pos bottom) {
   return FX_RECT(pdfium::base::checked_cast<int32_t>(left),
                  pdfium::base::checked_cast<int32_t>(top),
@@ -637,10 +613,6 @@
   return result;
 }
 
-void CFX_Font::AllocSubData(size_t size) {
-  m_pSubData.reset(FX_Alloc(uint8_t, size));
-}
-
 RetainPtr<CFX_GlyphCache> CFX_Font::GetOrCreateGlyphCache() const {
   if (!m_GlyphCache)
     m_GlyphCache = CFX_GEModule::Get()->GetFontCache()->GetGlyphCache(this);
@@ -655,24 +627,23 @@
                               int dest_width,
                               int weight) const {
   DCHECK(dest_width >= 0);
-  FXFT_MM_VarPtr pMasters = nullptr;
-  FT_Get_MM_Var(m_Face->GetRec(), &pMasters);
-  if (!pMasters)
+  ScopedFXFTMMVar variation_desc(m_Face->GetRec());
+  if (!variation_desc) {
     return;
+  }
 
   FT_Pos coords[2];
-  if (weight == 0)
-    coords[0] = FXFT_Get_MM_Axis_Def(FXFT_Get_MM_Axis(pMasters, 0)) / 65536;
-  else
+  if (weight == 0) {
+    coords[0] = variation_desc.GetAxisDefault(0) / 65536;
+  } else {
     coords[0] = weight;
+  }
 
   if (dest_width == 0) {
-    coords[1] = FXFT_Get_MM_Axis_Def(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
+    coords[1] = variation_desc.GetAxisDefault(1) / 65536;
   } else {
-    FT_Long min_param =
-        FXFT_Get_MM_Axis_Min(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
-    FT_Long max_param =
-        FXFT_Get_MM_Axis_Max(FXFT_Get_MM_Axis(pMasters, 1)) / 65536;
+    FT_Long min_param = variation_desc.GetAxisMin(1) / 65536;
+    FT_Long max_param = variation_desc.GetAxisMax(1) / 65536;
     coords[1] = min_param;
     FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
     FT_Load_Glyph(m_Face->GetRec(), glyph_index,
@@ -686,7 +657,6 @@
     FT_Pos max_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 /
                        FXFT_Get_Face_UnitsPerEM(m_Face->GetRec());
     if (max_width == min_width) {
-      FreeMMVar(m_Face->GetRec(), pMasters);
       return;
     }
     FT_Pos param = min_param + (max_param - min_param) *
@@ -694,7 +664,6 @@
                                    (max_width - min_width);
     coords[1] = param;
   }
-  FreeMMVar(m_Face->GetRec(), pMasters);
   FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords);
 }
 
diff --git a/core/fxge/cfx_font.h b/core/fxge/cfx_font.h
index bf94874..d1cc83d 100644
--- a/core/fxge/cfx_font.h
+++ b/core/fxge/cfx_font.h
@@ -16,12 +16,12 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxge/cfx_face.h"
 #include "core/fxge/freetype/fx_freetype.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #if defined(_SKIA_SUPPORT_)
 #include "core/fxge/fx_font.h"
@@ -128,8 +128,6 @@
   absl::optional<FX_RECT> GetBBox() const;
 
   bool IsEmbedded() const { return m_bEmbedded; }
-  void AllocSubData(size_t size);
-  uint8_t* GetSubData() const { return m_pSubData.get(); }
   FontType GetFontType() const { return m_FontType; }
   void SetFontType(FontType type) { m_FontType = type; }
   uint64_t GetObjectTag() const { return m_ObjectTag; }
@@ -166,7 +164,6 @@
   mutable RetainPtr<CFX_Face> m_Face;
   mutable RetainPtr<CFX_GlyphCache> m_GlyphCache;
   std::unique_ptr<CFX_SubstFont> m_pSubstFont;
-  std::unique_ptr<uint8_t, FxFreeDeleter> m_pSubData;
   DataVector<uint8_t> m_FontDataAllocation;
   pdfium::span<uint8_t> m_FontData;
   FontType m_FontType = FontType::kUnknown;
@@ -174,7 +171,7 @@
   bool m_bEmbedded = false;
   bool m_bVertical = false;
 #if BUILDFLAG(IS_APPLE)
-  void* m_pPlatformFont = nullptr;
+  UNOWNED_PTR_EXCLUSION void* m_pPlatformFont = nullptr;
 #endif
 };
 
diff --git a/core/fxge/cfx_fontmapper.cpp b/core/fxge/cfx_fontmapper.cpp
index 34c70a8..63173b6 100644
--- a/core/fxge/cfx_fontmapper.cpp
+++ b/core/fxge/cfx_fontmapper.cpp
@@ -19,13 +19,13 @@
 #include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/cfx_substfont.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/systemfontinfo_iface.h"
 #include "third_party/base/check_op.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
@@ -390,7 +390,7 @@
 
  private:
   UnownedPtr<SystemFontInfoIface> const font_info_;
-  void* const font_;
+  UNOWNED_PTR_EXCLUSION void* const font_;  // void type incompatible.
 };
 
 }  // namespace
@@ -715,7 +715,7 @@
   }
 
   if (Charset == FX_Charset::kSymbol) {
-#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_WIN)
     if (subst_name == "Symbol") {
       subst_font->m_Family = "Chrome Symbol";
       subst_font->m_Charset = FX_Charset::kSymbol;
diff --git a/core/fxge/cfx_fontmgr.h b/core/fxge/cfx_fontmgr.h
index 31539aa..2936e26 100644
--- a/core/fxge/cfx_fontmgr.h
+++ b/core/fxge/cfx_fontmgr.h
@@ -20,7 +20,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_face.h"
 #include "core/fxge/freetype/fx_freetype.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_FontMapper;
 
@@ -29,7 +29,6 @@
   class FontDesc final : public Retainable, public Observable {
    public:
     CONSTRUCT_VIA_MAKE_RETAIN;
-    ~FontDesc() override;
 
     pdfium::span<const uint8_t> FontData() const { return m_pFontData; }
     void SetFace(size_t index, CFX_Face* face);
@@ -37,6 +36,7 @@
 
    private:
     explicit FontDesc(FixedUninitDataVector<uint8_t> data);
+    ~FontDesc() override;
 
     const FixedUninitDataVector<uint8_t> m_pFontData;
     ObservedPtr<CFX_Face> m_TTCFaces[16];
diff --git a/core/fxge/cfx_gemodule.h b/core/fxge/cfx_gemodule.h
index f1f00f6..f6846ce 100644
--- a/core/fxge/cfx_gemodule.h
+++ b/core/fxge/cfx_gemodule.h
@@ -12,9 +12,10 @@
 #include <memory>
 
 #include "build/build_config.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 
 #if BUILDFLAG(IS_APPLE)
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #endif
 
 class CFX_FontCache;
@@ -52,7 +53,9 @@
   std::unique_ptr<PlatformIface> const m_pPlatform;
   std::unique_ptr<CFX_FontMgr> const m_pFontMgr;
   std::unique_ptr<CFX_FontCache> const m_pFontCache;
-  const char** const m_pUserFontPaths;
+
+  // Exclude because taken from public API.
+  UNOWNED_PTR_EXCLUSION const char** const m_pUserFontPaths;
 };
 
 #endif  // CORE_FXGE_CFX_GEMODULE_H_
diff --git a/core/fxge/cfx_glyphcache.cpp b/core/fxge/cfx_glyphcache.cpp
index 1f12d7b..ba308d4 100644
--- a/core/fxge/cfx_glyphcache.cpp
+++ b/core/fxge/cfx_glyphcache.cpp
@@ -204,7 +204,7 @@
                                         : FXDIB_Format::k8bppMask);
   int dest_pitch = pGlyphBitmap->GetBitmap()->GetPitch();
   int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
-  uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetBuffer().data();
+  uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetWritableBuffer().data();
   uint8_t* pSrcBuf = static_cast<uint8_t*>(
       FXFT_Get_Bitmap_Buffer(FXFT_Get_Glyph_Bitmap(GetFaceRec())));
   if (anti_alias != FT_RENDER_MODE_MONO &&
diff --git a/core/fxge/cfx_glyphcache.h b/core/fxge/cfx_glyphcache.h
index ff2e740..c71ae3c 100644
--- a/core/fxge/cfx_glyphcache.h
+++ b/core/fxge/cfx_glyphcache.h
@@ -18,7 +18,7 @@
 
 #if defined(_SKIA_SUPPORT_)
 #include "core/fxge/fx_font.h"
-#include "third_party/skia/include/core/SkTypeface.h"  // nogncheck
+#include "third_party/skia/include/core/SkRefCnt.h"  // nogncheck
 #endif
 
 class CFX_Font;
@@ -30,7 +30,6 @@
 class CFX_GlyphCache final : public Retainable, public Observable {
  public:
   CONSTRUCT_VIA_MAKE_RETAIN;
-  ~CFX_GlyphCache() override;
 
   const CFX_GlyphBitmap* LoadGlyphBitmap(const CFX_Font* pFont,
                                          uint32_t glyph_index,
@@ -56,6 +55,7 @@
 
  private:
   explicit CFX_GlyphCache(RetainPtr<CFX_Face> face);
+  ~CFX_GlyphCache() override;
 
   using SizeGlyphCache = std::map<uint32_t, std::unique_ptr<CFX_GlyphBitmap>>;
   // <glyph_index, width, weight, angle, vertical>
diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp
index d39a02f..2280f6f 100644
--- a/core/fxge/cfx_renderdevice.cpp
+++ b/core/fxge/cfx_renderdevice.cpp
@@ -33,7 +33,7 @@
 #include "core/fxge/text_glyph_pos.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 #if defined(_SKIA_SUPPORT_)
 #include "third_party/skia/include/core/SkTypes.h"  // nogncheck
@@ -426,7 +426,7 @@
   for (size_t i = 0; i < points.size(); i++) {
     CFX_Path::Point::Type point_type = points[i].m_Type;
     if (point_type == CFX_Path::Point::Type::kMove) {
-      DCHECK_EQ(0, i);
+      DCHECK_EQ(0u, i);
       continue;
     }
 
@@ -1186,14 +1186,6 @@
                          end_col, normalize, x_subpixel, a, r, g, b);
   }
 
-#if defined(_SKIA_SUPPORT_)
-  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
-    // DrawNormalTextHelper() can result in unpremultiplied bitmaps for
-    // rendering glyphs. Make sure `bitmap` is premultiplied before proceeding.
-    bitmap->PreMultiply();
-  }
-#endif
-
   if (bitmap->IsMaskFormat())
     SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color);
   else
@@ -1349,7 +1341,6 @@
   const float fHalfWidth = fWidth / 2.0f;
 
   switch (nStyle) {
-    default:
     case BorderStyle::kSolid: {
       CFX_Path path;
       path.AppendRect(fLeft, fBottom, fRight, fTop);
diff --git a/core/fxge/cfx_renderdevice.h b/core/fxge/cfx_renderdevice.h
index e1d9487..8e6e380 100644
--- a/core/fxge/cfx_renderdevice.h
+++ b/core/fxge/cfx_renderdevice.h
@@ -18,7 +18,7 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/render_defines.h"
 #include "core/fxge/renderdevicedriver_iface.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
@@ -215,12 +215,12 @@
   bool MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask);
 
 #if defined(_SKIA_SUPPORT_)
-  virtual bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
-                               const RetainPtr<CFX_DIBBase>& pMask,
-                               int left,
-                               int top,
-                               int bitmap_alpha,
-                               BlendMode blend_type);
+  bool SetBitsWithMask(const RetainPtr<CFX_DIBBase>& pBitmap,
+                       const RetainPtr<CFX_DIBBase>& pMask,
+                       int left,
+                       int top,
+                       int bitmap_alpha,
+                       BlendMode blend_type);
 #endif
 
  protected:
diff --git a/core/fxge/dib/cfx_bitmapcomposer.cpp b/core/fxge/dib/cfx_bitmapcomposer.cpp
index 009a618..e924034 100644
--- a/core/fxge/dib/cfx_bitmapcomposer.cpp
+++ b/core/fxge/dib/cfx_bitmapcomposer.cpp
@@ -131,7 +131,7 @@
   int Bpp = m_pBitmap->GetBPP() / 8;
   int dest_pitch = m_pBitmap->GetPitch();
   int dest_x = m_DestLeft + (m_bFlipX ? (m_DestWidth - line - 1) : line);
-  pdfium::span<uint8_t> dest_span = m_pBitmap->GetBuffer();
+  pdfium::span<uint8_t> dest_span = m_pBitmap->GetWritableBuffer();
   if (!dest_span.empty()) {
     const size_t dest_x_offset = Fx2DSizeOrDie(dest_x, Bpp);
     const size_t dest_y_offset = Fx2DSizeOrDie(m_DestTop, dest_pitch);
diff --git a/core/fxge/dib/cfx_bitmapstorer.h b/core/fxge/dib/cfx_bitmapstorer.h
index de934e2..7a5e3ff 100644
--- a/core/fxge/dib/cfx_bitmapstorer.h
+++ b/core/fxge/dib/cfx_bitmapstorer.h
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/scanlinecomposer_iface.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBitmap;
 
diff --git a/core/fxge/dib/cfx_dibbase.cpp b/core/fxge/dib/cfx_dibbase.cpp
index 07c7b74..97f8440 100644
--- a/core/fxge/dib/cfx_dibbase.cpp
+++ b/core/fxge/dib/cfx_dibbase.cpp
@@ -26,8 +26,8 @@
 #include "core/fxge/dib/cfx_imagetransformer.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
 
 namespace {
 
@@ -620,8 +620,8 @@
 
 CFX_DIBBase::~CFX_DIBBase() = default;
 
-pdfium::span<uint8_t> CFX_DIBBase::GetBuffer() const {
-  return pdfium::span<uint8_t>();
+pdfium::span<const uint8_t> CFX_DIBBase::GetBuffer() const {
+  return pdfium::span<const uint8_t>();
 }
 
 bool CFX_DIBBase::SkipToScanline(int line, PauseIndicatorIface* pPause) const {
@@ -956,8 +956,9 @@
 
   RetainPtr<const CFX_DIBBase> holder(this);
   DataVector<uint32_t> pal_8bpp;
-  if (!ConvertBuffer(dest_format, pClone->GetBuffer(), pClone->GetPitch(),
-                     m_Width, m_Height, holder, 0, 0, &pal_8bpp)) {
+  if (!ConvertBuffer(dest_format, pClone->GetWritableBuffer(),
+                     pClone->GetPitch(), m_Width, m_Height, holder, 0, 0,
+                     &pal_8bpp)) {
     return nullptr;
   }
   if (!pal_8bpp.empty())
@@ -979,8 +980,8 @@
 
   pTransBitmap->SetPalette(GetPaletteSpan());
   const int dest_pitch = pTransBitmap->GetPitch();
-  pdfium::span<uint8_t> dest_span =
-      pTransBitmap->GetBuffer().first(Fx2DSizeOrDie(dest_pitch, result_height));
+  pdfium::span<uint8_t> dest_span = pTransBitmap->GetWritableBuffer().first(
+      Fx2DSizeOrDie(dest_pitch, result_height));
   const size_t dest_last_row_offset =
       Fx2DSizeOrDie(dest_pitch, result_height - 1);
   const int row_start = bXFlip ? m_Height - dest_clip.right : dest_clip.left;
@@ -1128,7 +1129,6 @@
                                 height, pSrcBitmap, src_left, src_top);
     }
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
 }
diff --git a/core/fxge/dib/cfx_dibbase.h b/core/fxge/dib/cfx_dibbase.h
index 9f0deeb..ebf1eac 100644
--- a/core/fxge/dib/cfx_dibbase.h
+++ b/core/fxge/dib/cfx_dibbase.h
@@ -12,7 +12,11 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
+
+#if defined(_SKIA_SUPPORT_)
+#include "third_party/skia/include/core/SkRefCnt.h"  // nogncheck
+#endif
 
 class CFX_ClipRgn;
 class CFX_DIBitmap;
@@ -20,6 +24,10 @@
 class PauseIndicatorIface;
 struct FX_RECT;
 
+#if defined(_SKIA_SUPPORT_)
+class SkImage;
+#endif  // defined(_SKIA_SUPPORT_)
+
 // Base class for all Device-Independent Bitmaps.
 class CFX_DIBBase : public Retainable {
  public:
@@ -32,17 +40,11 @@
 
   static constexpr uint32_t kPaletteSize = 256;
 
-  ~CFX_DIBBase() override;
-
-  virtual pdfium::span<uint8_t> GetBuffer() const;
+  virtual pdfium::span<const uint8_t> GetBuffer() const;
   virtual pdfium::span<const uint8_t> GetScanline(int line) const = 0;
   virtual bool SkipToScanline(int line, PauseIndicatorIface* pPause) const;
   virtual size_t GetEstimatedImageMemoryBurden() const;
 
-  pdfium::span<uint8_t> GetWritableScanline(int line) {
-    pdfium::span<const uint8_t> src = GetScanline(line);
-    return {const_cast<uint8_t*>(src.data()), src.size()};
-  }
   int GetWidth() const { return m_Width; }
   int GetHeight() const { return m_Height; }
   uint32_t GetPitch() const { return m_Pitch; }
@@ -87,8 +89,17 @@
                       int& src_top,
                       const CFX_ClipRgn* pClipRgn) const;
 
+#if defined(_SKIA_SUPPORT_)
+  // Realizes an `SkImage` from this DIB.
+  //
+  // This may share the underlying pixels, in which case, this DIB should not be
+  // modified during the lifetime of the `SkImage`.
+  virtual sk_sp<SkImage> RealizeSkImage() const;
+#endif  // defined(_SKIA_SUPPORT_)
+
  protected:
   CFX_DIBBase();
+  ~CFX_DIBBase() override;
 
   static bool ConvertBuffer(FXDIB_Format dest_format,
                             pdfium::span<uint8_t> dest_buf,
@@ -100,6 +111,11 @@
                             int src_top,
                             DataVector<uint32_t>* pal);
 
+#if defined(_SKIA_SUPPORT_)
+  // Whether alpha is premultiplied (if `IsAlphaFormat()`).
+  virtual bool IsPremultiplied() const;
+#endif  // defined(_SKIA_SUPPORT_)
+
   RetainPtr<CFX_DIBitmap> ClipToInternal(const FX_RECT* pClip) const;
   void BuildPalette();
   int FindPalette(uint32_t color) const;
diff --git a/core/fxge/dib/cfx_dibextractor.cpp b/core/fxge/dib/cfx_dibextractor.cpp
deleted file mode 100644
index 9dcfbec..0000000
--- a/core/fxge/dib/cfx_dibextractor.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#include "core/fxge/dib/cfx_dibextractor.h"
-
-#include "core/fxge/dib/cfx_dibbase.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
-
-CFX_DIBExtractor::CFX_DIBExtractor(const RetainPtr<CFX_DIBBase>& pSrc) {
-  if (pSrc->GetBuffer().empty()) {
-    m_pBitmap = pSrc->Realize();
-    return;
-  }
-  m_pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  if (!m_pBitmap->Create(pSrc->GetWidth(), pSrc->GetHeight(), pSrc->GetFormat(),
-                         pSrc->GetBuffer().data(),
-                         /*pitch=*/0)) {
-    m_pBitmap.Reset();
-    return;
-  }
-
-  m_pBitmap->SetPalette(pSrc->GetPaletteSpan());
-}
-
-CFX_DIBExtractor::~CFX_DIBExtractor() = default;
diff --git a/core/fxge/dib/cfx_dibextractor.h b/core/fxge/dib/cfx_dibextractor.h
deleted file mode 100644
index 455eca1..0000000
--- a/core/fxge/dib/cfx_dibextractor.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The PDFium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
-
-#ifndef CORE_FXGE_DIB_CFX_DIBEXTRACTOR_H_
-#define CORE_FXGE_DIB_CFX_DIBEXTRACTOR_H_
-
-#include "core/fxcrt/retain_ptr.h"
-
-class CFX_DIBBase;
-class CFX_DIBitmap;
-
-class CFX_DIBExtractor {
- public:
-  explicit CFX_DIBExtractor(const RetainPtr<CFX_DIBBase>& pSrc);
-  ~CFX_DIBExtractor();
-
-  RetainPtr<CFX_DIBitmap> GetBitmap() { return m_pBitmap; }
-
- private:
-  RetainPtr<CFX_DIBitmap> m_pBitmap;
-};
-
-#endif  // CORE_FXGE_DIB_CFX_DIBEXTRACTOR_H_
diff --git a/core/fxge/dib/cfx_dibitmap.cpp b/core/fxge/dib/cfx_dibitmap.cpp
index 62881a6..6df1eee 100644
--- a/core/fxge/dib/cfx_dibitmap.cpp
+++ b/core/fxge/dib/cfx_dibitmap.cpp
@@ -85,9 +85,9 @@
 
 CFX_DIBitmap::~CFX_DIBitmap() = default;
 
-pdfium::span<uint8_t> CFX_DIBitmap::GetBuffer() const {
+pdfium::span<const uint8_t> CFX_DIBitmap::GetBuffer() const {
   if (!m_pBuffer)
-    return pdfium::span<uint8_t>();
+    return pdfium::span<const uint8_t>();
 
   return {m_pBuffer.Get(), m_Height * m_Pitch};
 }
@@ -237,7 +237,7 @@
   if (!offset.IsValid())
     return false;
 
-  pdfium::span<uint8_t> dest_buf = GetBuffer().subspan(
+  pdfium::span<uint8_t> dest_buf = GetWritableBuffer().subspan(
       dest_top * m_Pitch + static_cast<uint32_t>(offset.ValueOrDie()));
   DataVector<uint32_t> d_plt;
   return ConvertBuffer(dest_format, dest_buf, m_Pitch, width, height,
@@ -389,11 +389,9 @@
 }
 
 bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& pSrcBitmap) {
-  if (!m_pBuffer)
-    return false;
+  CHECK(pSrcBitmap->IsMaskFormat());
 
-  if (!pSrcBitmap->IsMaskFormat()) {
-    NOTREACHED();
+  if (!m_pBuffer) {
     return false;
   }
 
@@ -698,11 +696,8 @@
                                    BlendMode blend_type,
                                    const CFX_ClipRgn* pClipRgn,
                                    bool bRgbByteOrder) {
-  if (pSrcBitmap->IsMaskFormat()) {
-    // Should have called CompositeMask().
-    NOTREACHED();
-    return false;
-  }
+  // Should have called CompositeMask().
+  CHECK(!pSrcBitmap->IsMaskFormat());
 
   if (!m_pBuffer)
     return false;
@@ -765,11 +760,8 @@
                                  BlendMode blend_type,
                                  const CFX_ClipRgn* pClipRgn,
                                  bool bRgbByteOrder) {
-  if (!pMask->IsMaskFormat()) {
-    // Should have called CompositeBitmap().
-    NOTREACHED();
-    return false;
-  }
+  // Should have called CompositeBitmap().
+  CHECK(pMask->IsMaskFormat());
 
   if (!m_pBuffer)
     return false;
@@ -926,11 +918,7 @@
     return true;
   }
 
-  if (GetBppFromFormat(m_Format) < 24) {
-    NOTREACHED();
-    return false;
-  }
-
+  CHECK_GE(GetBppFromFormat(m_Format), 24);
   color_p[3] = static_cast<uint8_t>(src_alpha);
   int Bpp = GetBppFromFormat(m_Format) / 8;
   const bool bAlpha = IsAlphaFormat();
diff --git a/core/fxge/dib/cfx_dibitmap.h b/core/fxge/dib/cfx_dibitmap.h
index 5817155..34f5887 100644
--- a/core/fxge/dib/cfx_dibitmap.h
+++ b/core/fxge/dib/cfx_dibitmap.h
@@ -13,6 +13,7 @@
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/fx_dib.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBitmap final : public CFX_DIBBase {
  public:
@@ -33,10 +34,20 @@
   bool Copy(const RetainPtr<CFX_DIBBase>& pSrc);
 
   // CFX_DIBBase
-  pdfium::span<uint8_t> GetBuffer() const override;
+  pdfium::span<const uint8_t> GetBuffer() const override;
   pdfium::span<const uint8_t> GetScanline(int line) const override;
   size_t GetEstimatedImageMemoryBurden() const override;
 
+  pdfium::span<uint8_t> GetWritableBuffer() {
+    pdfium::span<const uint8_t> src = GetBuffer();
+    return {const_cast<uint8_t*>(src.data()), src.size()};
+  }
+
+  pdfium::span<uint8_t> GetWritableScanline(int line) {
+    pdfium::span<const uint8_t> src = GetScanline(line);
+    return {const_cast<uint8_t*>(src.data()), src.size()};
+  }
+
   void TakeOver(RetainPtr<CFX_DIBitmap>&& pSrcBitmap);
   bool ConvertFormat(FXDIB_Format format);
   void Clear(uint32_t color);
@@ -114,21 +125,19 @@
                                                             uint32_t pitch);
 
 #if defined(_SKIA_SUPPORT_)
-  // Converts to pre-multiplied alpha if necessary.
-  void PreMultiply();
-
   // Converts to un-pre-multiplied alpha if necessary.
   void UnPreMultiply();
 
   // Forces pre-multiplied alpha without conversion.
   // TODO(crbug.com/pdfium/2011): Remove the need for this.
   void ForcePreMultiply();
-
-  // Forces un-pre-multiplied alpha without conversion.
-  // TODO(crbug.com/pdfium/2011): Remove the need for this.
-  void ForceUnPreMultiply();
 #endif
 
+ protected:
+#if defined(_SKIA_SUPPORT_)
+  bool IsPremultiplied() const override;
+#endif  // defined(_SKIA_SUPPORT_)
+
  private:
   enum class Channel : uint8_t { kRed, kAlpha };
 
diff --git a/core/fxge/dib/cfx_dibitmap_unittest.cpp b/core/fxge/dib/cfx_dibitmap_unittest.cpp
index ce79a01..ef448c2 100644
--- a/core/fxge/dib/cfx_dibitmap_unittest.cpp
+++ b/core/fxge/dib/cfx_dibitmap_unittest.cpp
@@ -10,7 +10,7 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
@@ -114,16 +114,6 @@
 }
 
 #if defined(_SKIA_SUPPORT_)
-TEST(CFX_DIBitmap, PreMultiply_FromCleared) {
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
-  FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff);
-
-  bitmap->PreMultiply();
-
-  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f));
-}
-
 TEST(CFX_DIBitmap, UnPreMultiply_FromCleared) {
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
@@ -134,21 +124,10 @@
   EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
 }
 
-TEST(CFX_DIBitmap, PreMultiply_FromPreMultiplied) {
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
-  bitmap->PreMultiply();
-  FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f);
-
-  bitmap->PreMultiply();
-
-  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f));
-}
-
 TEST(CFX_DIBitmap, UnPreMultiply_FromPreMultiplied) {
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
-  bitmap->PreMultiply();
+  bitmap->ForcePreMultiply();
   FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f);
 
   bitmap->UnPreMultiply();
@@ -156,17 +135,6 @@
   EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
 }
 
-TEST(CFX_DIBitmap, PreMultiply_FromUnPreMultiplied) {
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
-  bitmap->UnPreMultiply();
-  FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff);
-
-  bitmap->PreMultiply();
-
-  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f));
-}
-
 TEST(CFX_DIBitmap, UnPreMultiply_FromUnPreMultiplied) {
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
@@ -177,26 +145,4 @@
 
   EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
 }
-
-TEST(CFX_DIBitmap, ForcePreMultiply) {
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
-  FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f);
-
-  bitmap->ForcePreMultiply();
-
-  bitmap->PreMultiply();
-  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f));
-}
-
-TEST(CFX_DIBitmap, ForceUnPreMultiply) {
-  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
-  ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb));
-  FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff);
-
-  bitmap->ForceUnPreMultiply();
-
-  bitmap->UnPreMultiply();
-  EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f));
-}
 #endif  // defined(_SKIA_SUPPORT_)
diff --git a/core/fxge/dib/cfx_imagestretcher.cpp b/core/fxge/dib/cfx_imagestretcher.cpp
index f9b828a..1eafa40 100644
--- a/core/fxge/dib/cfx_imagestretcher.cpp
+++ b/core/fxge/dib/cfx_imagestretcher.cpp
@@ -13,7 +13,7 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
diff --git a/core/fxge/dib/cfx_imagetransformer.cpp b/core/fxge/dib/cfx_imagetransformer.cpp
index 904a7f5..12bc14d 100644
--- a/core/fxge/dib/cfx_imagetransformer.cpp
+++ b/core/fxge/dib/cfx_imagetransformer.cpp
@@ -157,7 +157,7 @@
         &m_Storer, m_pSrc, dest_height, dest_width, result_clip,
         m_ResampleOptions);
     m_Stretcher->Start();
-    m_type = kRotate;
+    m_type = StretchType::kRotate;
     return;
   }
   if (fabs(m_matrix.b) < kFix16 && fabs(m_matrix.c) < kFix16) {
@@ -170,7 +170,7 @@
         &m_Storer, m_pSrc, dest_width, dest_height, result_clip,
         m_ResampleOptions);
     m_Stretcher->Start();
-    m_type = kNormal;
+    m_type = StretchType::kNormal;
     return;
   }
 
@@ -200,32 +200,32 @@
       &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip,
       m_ResampleOptions);
   m_Stretcher->Start();
-  m_type = kOther;
+  m_type = StretchType::kOther;
 }
 
 CFX_ImageTransformer::~CFX_ImageTransformer() = default;
 
 bool CFX_ImageTransformer::Continue(PauseIndicatorIface* pPause) {
-  if (m_type == kNone)
+  if (m_type == StretchType::kNone) {
     return false;
+  }
 
   if (m_Stretcher->Continue(pPause))
     return true;
 
   switch (m_type) {
-    case kNormal:
-      break;
-    case kRotate:
+    case StretchType::kNone:
+      // Already handled separately at the beginning of this method.
+      NOTREACHED_NORETURN();
+    case StretchType::kNormal:
+      return false;
+    case StretchType::kRotate:
       ContinueRotate(pPause);
-      break;
-    case kOther:
+      return false;
+    case StretchType::kOther:
       ContinueOther(pPause);
-      break;
-    default:
-      NOTREACHED();
-      break;
+      return false;
   }
-  return false;
 }
 
 void CFX_ImageTransformer::ContinueRotate(PauseIndicatorIface* pPause) {
diff --git a/core/fxge/dib/cfx_imagetransformer.h b/core/fxge/dib/cfx_imagetransformer.h
index abd60c4..2329e9b 100644
--- a/core/fxge/dib/cfx_imagetransformer.h
+++ b/core/fxge/dib/cfx_imagetransformer.h
@@ -11,6 +11,7 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxge/dib/cfx_bitmapstorer.h"
 
 class CFX_DIBBase;
@@ -32,7 +33,7 @@
   };
 
   struct CalcData {
-    CFX_DIBitmap* bitmap;
+    UNOWNED_PTR_EXCLUSION CFX_DIBitmap* bitmap;  // POD struct.
     const CFX_Matrix& matrix;
     const uint8_t* buf;
     uint32_t pitch;
@@ -50,7 +51,7 @@
   RetainPtr<CFX_DIBitmap> DetachBitmap();
 
  private:
-  enum StretchType {
+  enum class StretchType {
     kNone,
     kNormal,
     kRotate,
@@ -72,7 +73,7 @@
   std::unique_ptr<CFX_ImageStretcher> m_Stretcher;
   CFX_BitmapStorer m_Storer;
   const FXDIB_ResampleOptions m_ResampleOptions;
-  StretchType m_type = kNone;
+  StretchType m_type = StretchType::kNone;
 };
 
 #endif  // CORE_FXGE_DIB_CFX_IMAGETRANSFORMER_H_
diff --git a/core/fxge/dib/cfx_scanlinecompositor.h b/core/fxge/dib/cfx_scanlinecompositor.h
index 9fa6a86..c7e95fd 100644
--- a/core/fxge/dib/cfx_scanlinecompositor.h
+++ b/core/fxge/dib/cfx_scanlinecompositor.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_ScanlineCompositor {
  public:
diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp
index e9629fc..920645a 100644
--- a/core/fxge/dib/cstretchengine.cpp
+++ b/core/fxge/dib/cstretchengine.cpp
@@ -21,7 +21,6 @@
 #include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/dib/scanlinecomposer_iface.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
 
 static_assert(
     std::is_trivially_destructible<CStretchEngine::PixelWeight>::value,
@@ -500,9 +499,9 @@
             int r = static_cast<uint32_t>(dest_r) * 255 / dest_a;
             int g = static_cast<uint32_t>(dest_g) * 255 / dest_a;
             int b = static_cast<uint32_t>(dest_b) * 255 / dest_a;
-            dest_scan[0] = pdfium::clamp(b, 0, 255);
-            dest_scan[1] = pdfium::clamp(g, 0, 255);
-            dest_scan[2] = pdfium::clamp(r, 0, 255);
+            dest_scan[0] = std::clamp(b, 0, 255);
+            dest_scan[1] = std::clamp(g, 0, 255);
+            dest_scan[2] = std::clamp(r, 0, 255);
           }
           dest_scan[3] = PixelFromFixed(dest_a);
           dest_scan += DestBpp;
diff --git a/core/fxge/dib/cstretchengine.h b/core/fxge/dib/cstretchengine.h
index 2ee1477..4f0d71f 100644
--- a/core/fxge/dib/cstretchengine.h
+++ b/core/fxge/dib/cstretchengine.h
@@ -17,7 +17,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/dib/fx_dib.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class PauseIndicatorIface;
diff --git a/core/fxge/dib/scanlinecomposer_iface.h b/core/fxge/dib/scanlinecomposer_iface.h
index 57baf9b..ef997cf 100644
--- a/core/fxge/dib/scanlinecomposer_iface.h
+++ b/core/fxge/dib/scanlinecomposer_iface.h
@@ -8,7 +8,7 @@
 #define CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_
 
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class ScanlineComposerIface {
  public:
diff --git a/core/fxge/freetype/fx_freetype.cpp b/core/fxge/freetype/fx_freetype.cpp
index 1c3deee..effd160 100644
--- a/core/fxge/freetype/fx_freetype.cpp
+++ b/core/fxge/freetype/fx_freetype.cpp
@@ -8,6 +8,9 @@
 
 #include <stdint.h>
 
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+
 #define DEFINE_PS_TABLES
 #include "third_party/freetype/include/pstables.h"
 
@@ -57,8 +60,36 @@
   return 0;
 }
 
+FT_MM_Var* GetVariationDescriptor(FXFT_FaceRec* face) {
+  FT_MM_Var* variation_desc = nullptr;
+  FT_Get_MM_Var(face, &variation_desc);
+  return variation_desc;
+}
+
 }  // namespace
 
+void FXFTMMVarDeleter::operator()(FT_MM_Var* variation_desc) {
+  FT_Done_MM_Var(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
+                 variation_desc);
+}
+
+ScopedFXFTMMVar::ScopedFXFTMMVar(FXFT_FaceRec* face)
+    : variation_desc_(GetVariationDescriptor(face)) {}
+
+ScopedFXFTMMVar::~ScopedFXFTMMVar() = default;
+
+FT_Pos ScopedFXFTMMVar::GetAxisDefault(size_t index) const {
+  return variation_desc_->axis[index].def;
+}
+
+FT_Long ScopedFXFTMMVar::GetAxisMin(size_t index) const {
+  return variation_desc_->axis[index].minimum;
+}
+
+FT_Long ScopedFXFTMMVar::GetAxisMax(size_t index) const {
+  return variation_desc_->axis[index].maximum;
+}
+
 int FXFT_unicode_from_adobe_name(const char* glyph_name) {
   /* If the name begins with `uni', then the glyph name may be a */
   /* hard-coded unicode character code.                          */
diff --git a/core/fxge/freetype/fx_freetype.h b/core/fxge/freetype/fx_freetype.h
index 3da0372..96c3d2f 100644
--- a/core/fxge/freetype/fx_freetype.h
+++ b/core/fxge/freetype/fx_freetype.h
@@ -21,7 +21,6 @@
 using FXFT_LibraryRec = struct FT_LibraryRec_;
 using FXFT_FaceRec = struct FT_FaceRec_;
 using FXFT_StreamRec = struct FT_StreamRec_;
-using FXFT_MM_VarPtr = FT_MM_Var*;
 
 struct FXFTFaceRecDeleter {
   inline void operator()(FXFT_FaceRec* pRec) { FT_Done_Face(pRec); }
@@ -31,10 +30,29 @@
   inline void operator()(FXFT_LibraryRec* pRec) { FT_Done_FreeType(pRec); }
 };
 
+struct FXFTMMVarDeleter {
+  void operator()(FT_MM_Var* variation_desc);
+};
+
 using ScopedFXFTFaceRec = std::unique_ptr<FXFT_FaceRec, FXFTFaceRecDeleter>;
 using ScopedFXFTLibraryRec =
     std::unique_ptr<FXFT_LibraryRec, FXFTLibraryRecDeleter>;
 
+class ScopedFXFTMMVar {
+ public:
+  explicit ScopedFXFTMMVar(FXFT_FaceRec* face);
+  ~ScopedFXFTMMVar();
+
+  explicit operator bool() const { return !!variation_desc_; }
+
+  FT_Pos GetAxisDefault(size_t index) const;
+  FT_Long GetAxisMin(size_t index) const;
+  FT_Long GetAxisMax(size_t index) const;
+
+ private:
+  std::unique_ptr<FT_MM_Var, FXFTMMVarDeleter> const variation_desc_;
+};
+
 #define FXFT_Select_Charmap(face, encoding) \
   FT_Select_Charmap(face, static_cast<FT_Encoding>(encoding))
 #define FXFT_Render_Glyph(face, mode) \
@@ -72,11 +90,6 @@
 #define FXFT_Get_Face_Ascender(face) (face)->ascender
 #define FXFT_Get_Face_Descender(face) (face)->descender
 #define FXFT_Get_Glyph_HoriAdvance(face) (face)->glyph->metrics.horiAdvance
-#define FXFT_Get_MM_Axis(var, index) (var)->axis[index]
-#define FXFT_Get_MM_Axis_Min(axis) (axis).minimum
-#define FXFT_Get_MM_Axis_Max(axis) (axis).maximum
-#define FXFT_Get_MM_Axis_Def(axis) (axis).def
-#define FXFT_Free(face, p) (face)->memory->free((face)->memory, p)
 #define FXFT_Get_Glyph_Outline(face) &((face)->glyph->outline)
 #define FXFT_Get_Glyph_Bitmap(face) (face)->glyph->bitmap
 #define FXFT_Get_Bitmap_Width(bitmap) (bitmap).width
diff --git a/core/fxge/fx_font.h b/core/fxge/fx_font.h
index 60a27ed..7770f7f 100644
--- a/core/fxge/fx_font.h
+++ b/core/fxge/fx_font.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 /* Font pitch and family flags */
 #define FXFONT_FF_FIXEDPITCH (1 << 0)
diff --git a/core/fxge/renderdevicedriver_iface.h b/core/fxge/renderdevicedriver_iface.h
index a25eca3..09dd93c 100644
--- a/core/fxge/renderdevicedriver_iface.h
+++ b/core/fxge/renderdevicedriver_iface.h
@@ -14,7 +14,7 @@
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
diff --git a/core/fxge/skia/DEPS b/core/fxge/skia/DEPS
index 17c0265..9927648 100644
--- a/core/fxge/skia/DEPS
+++ b/core/fxge/skia/DEPS
@@ -1,5 +1,9 @@
 include_rules = [
-  '+fpdfsdk',
-  '+public',
   '+third_party/skia/include',
 ]
+
+specific_include_rules = {
+  'fx_skia_device_embeddertest.cpp': [
+    '+fpdfsdk',
+  ]
+}
diff --git a/core/fxge/skia/cfx_dibbase_skia.cpp b/core/fxge/skia/cfx_dibbase_skia.cpp
new file mode 100644
index 0000000..417aa93
--- /dev/null
+++ b/core/fxge/skia/cfx_dibbase_skia.cpp
@@ -0,0 +1,288 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxge/dib/cfx_dibbase.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/calculate_pitch.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/notreached.h"
+#include "third_party/skia/include/core/SkAlphaType.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkColorType.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPixmap.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace {
+
+// Releases `CFX_DIBBase` "leaked" by `CreateSkiaImageFromDib()`.
+void ReleaseRetainedHeldBySkImage(const void* /*pixels*/,
+                                  SkImages::ReleaseContext context) {
+  RetainPtr<const CFX_DIBBase> retained;
+  retained.Unleak(reinterpret_cast<const CFX_DIBBase*>(context));
+}
+
+// Creates an `SkImage` from a `CFX_DIBBase`, sharing the underlying pixels if
+// possible.
+//
+// Note that an `SkImage` must be immutable, so if sharing pixels, they must not
+// be modified during the lifetime of the `SkImage`.
+sk_sp<SkImage> CreateSkiaImageFromDib(const CFX_DIBBase* source,
+                                      SkColorType color_type,
+                                      SkAlphaType alpha_type) {
+  // Make sure the DIB is backed by a buffer.
+  RetainPtr<const CFX_DIBBase> retained;
+  if (source->GetBuffer().empty()) {
+    retained = source->Realize();
+    if (!retained) {
+      return nullptr;
+    }
+    DCHECK(!retained->GetBuffer().empty());
+  } else {
+    retained.Reset(source);
+  }
+
+  // Convert unowned pointer to a retained pointer, then "leak" to `SkImage`.
+  source = retained.Leak();
+  SkImageInfo info = SkImageInfo::Make(source->GetWidth(), source->GetHeight(),
+                                       color_type, alpha_type);
+  return SkImages::RasterFromPixmap(
+      SkPixmap(info, source->GetBuffer().data(), source->GetPitch()),
+      /*rasterReleaseProc=*/ReleaseRetainedHeldBySkImage,
+      /*releaseContext=*/const_cast<CFX_DIBBase*>(source));
+}
+
+// Releases allocated memory "leaked" by `CreateSkiaImageFromTransformedDib()`.
+void ReleaseAllocatedHeldBySkImage(const void* pixels,
+                                   SkImages::ReleaseContext /*context*/) {
+  FX_Free(const_cast<void*>(pixels));
+}
+
+// Template defining traits of a pixel transform function.
+template <size_t source_bits_per_pixel, typename PixelTransform>
+class PixelTransformTraits;
+
+template <typename PixelTransform>
+class PixelTransformTraits<1, PixelTransform> {
+ public:
+  using Result = std::invoke_result_t<PixelTransform, bool>;
+
+  static Result Invoke(PixelTransform&& pixel_transform,
+                       const uint8_t* scanline,
+                       size_t column) {
+    uint8_t kMask = 1 << (7 - column % 8);
+    return pixel_transform(!!(scanline[column / 8] & kMask));
+  }
+};
+
+template <typename PixelTransform>
+class PixelTransformTraits<8, PixelTransform> {
+ public:
+  using Result = std::invoke_result_t<PixelTransform, uint8_t>;
+
+  static Result Invoke(PixelTransform&& pixel_transform,
+                       const uint8_t* scanline,
+                       size_t column) {
+    return pixel_transform(scanline[column]);
+  }
+};
+
+template <typename PixelTransform>
+class PixelTransformTraits<24, PixelTransform> {
+ public:
+  using Result =
+      std::invoke_result_t<PixelTransform, uint8_t, uint8_t, uint8_t>;
+
+  static Result Invoke(PixelTransform&& pixel_transform,
+                       const uint8_t* scanline,
+                       size_t column) {
+    size_t offset = column * 3;
+    return pixel_transform(scanline[offset + 2], scanline[offset + 1],
+                           scanline[offset]);
+  }
+};
+
+void ValidateScanlineSize(pdfium::span<const uint8_t> scanline,
+                          size_t min_row_bytes) {
+  DCHECK_GE(scanline.size(), min_row_bytes);
+}
+
+void ValidateBufferSize(pdfium::span<const uint8_t> buffer,
+                        const CFX_DIBBase& source) {
+#if DCHECK_IS_ON()
+  if (source.GetHeight() == 0) {
+    return;
+  }
+
+  FX_SAFE_SIZE_T buffer_size = source.GetHeight() - 1;
+  buffer_size *= source.GetPitch();
+  buffer_size += fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1,
+                                            source.GetWidth());
+
+  DCHECK_GE(buffer.size(), buffer_size.ValueOrDie());
+#endif  // DCHECK_IS_ON()
+}
+
+// Creates an `SkImage` from a `CFX_DIBBase`, transforming the source pixels
+// using `pixel_transform`.
+//
+// TODO(crbug.com/pdfium/2048): Consolidate with `CFX_DIBBase::ConvertBuffer()`.
+template <size_t source_bits_per_pixel, typename PixelTransform>
+sk_sp<SkImage> CreateSkiaImageFromTransformedDib(
+    const CFX_DIBBase& source,
+    SkColorType color_type,
+    SkAlphaType alpha_type,
+    PixelTransform&& pixel_transform) {
+  using Traits = PixelTransformTraits<source_bits_per_pixel, PixelTransform>;
+  using Result = typename Traits::Result;
+
+  // Allocate output buffer.
+  const int width = source.GetWidth();
+  const int height = source.GetHeight();
+  SkImageInfo info = SkImageInfo::Make(width, height, color_type, alpha_type);
+  DCHECK_EQ(info.minRowBytes(), width * sizeof(Result));
+
+  size_t output_size = Fx2DSizeOrDie(info.minRowBytes(), height);
+  std::unique_ptr<void, FxFreeDeleter> output(
+      FX_TryAlloc(uint8_t, output_size));
+  if (!output) {
+    return nullptr;
+  }
+
+  // Transform source pixels to output pixels.
+  pdfium::span<const uint8_t> source_buffer = source.GetBuffer();
+  Result* output_cursor = reinterpret_cast<Result*>(output.get());
+  if (source_buffer.empty()) {
+    // No buffer; iterate by individual scanline.
+    const size_t min_row_bytes =
+        fxge::CalculatePitch8OrDie(source.GetBPP(), /*components=*/1, width);
+    DCHECK_LE(min_row_bytes, source.GetPitch());
+
+    int line = 0;
+    for (int row = 0; row < height; ++row) {
+      pdfium::span<const uint8_t> scanline = source.GetScanline(line++);
+      ValidateScanlineSize(scanline, min_row_bytes);
+
+      for (int column = 0; column < width; ++column) {
+        *output_cursor++ =
+            Traits::Invoke(std::forward<PixelTransform>(pixel_transform),
+                           scanline.data(), column);
+      }
+    }
+  } else {
+    // Iterate over the entire buffer.
+    ValidateBufferSize(source_buffer, source);
+    const size_t row_bytes = source.GetPitch();
+
+    const uint8_t* next_scanline = source_buffer.data();
+    for (int row = 0; row < height; ++row) {
+      const uint8_t* scanline = next_scanline;
+      next_scanline += row_bytes;
+
+      for (int column = 0; column < width; ++column) {
+        *output_cursor++ = Traits::Invoke(
+            std::forward<PixelTransform>(pixel_transform), scanline, column);
+      }
+    }
+  }
+
+  // "Leak" allocated memory to `SkImage`.
+  return SkImages::RasterFromPixmap(
+      SkPixmap(info, output.release(), info.minRowBytes()),
+      /*rasterReleaseProc=*/ReleaseAllocatedHeldBySkImage,
+      /*releaseContext=*/nullptr);
+}
+
+bool IsRGBColorGrayScale(uint32_t color) {
+  return FXARGB_R(color) == FXARGB_G(color) &&
+         FXARGB_R(color) == FXARGB_B(color);
+}
+
+}  // namespace
+
+sk_sp<SkImage> CFX_DIBBase::RealizeSkImage() const {
+  switch (GetBPP()) {
+    case 1: {
+      // By default, the two colors for grayscale are 0xFF and 0x00 unless they
+      // are specified in the palette.
+      uint8_t color0 = 0x00;
+      uint8_t color1 = 0xFF;
+
+      if (GetFormat() == FXDIB_Format::k1bppRgb && HasPalette()) {
+        uint32_t palette_color0 = GetPaletteArgb(0);
+        uint32_t palette_color1 = GetPaletteArgb(1);
+        bool use_gray_colors = IsRGBColorGrayScale(palette_color0) &&
+                               IsRGBColorGrayScale(palette_color1);
+        if (!use_gray_colors) {
+          return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/1>(
+              *this, kBGRA_8888_SkColorType, kPremul_SkAlphaType,
+              [palette_color0, palette_color1](bool bit) {
+                return bit ? palette_color1 : palette_color0;
+              });
+        }
+
+        color0 = FXARGB_R(palette_color0);
+        color1 = FXARGB_R(palette_color1);
+      }
+
+      return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/1>(
+          *this, IsMaskFormat() ? kAlpha_8_SkColorType : kGray_8_SkColorType,
+          kPremul_SkAlphaType,
+          [color0, color1](bool bit) { return bit ? color1 : color0; });
+    }
+
+    case 8:
+      // we upscale ctables to 32bit.
+      if (HasPalette()) {
+        return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/8>(
+            *this, kBGRA_8888_SkColorType, kPremul_SkAlphaType,
+            [palette = GetPaletteSpan().first(GetRequiredPaletteSize())](
+                uint8_t index) {
+              if (index >= palette.size()) {
+                index = 0;
+              }
+              return palette[index];
+            });
+      }
+      return CreateSkiaImageFromDib(
+          this, IsMaskFormat() ? kAlpha_8_SkColorType : kGray_8_SkColorType,
+          kPremul_SkAlphaType);
+
+    case 24:
+      return CreateSkiaImageFromTransformedDib</*source_bits_per_pixel=*/24>(
+          *this, kBGRA_8888_SkColorType, kOpaque_SkAlphaType,
+          [](uint8_t red, uint8_t green, uint8_t blue) {
+            return SkPackARGB32NoCheck(0xFF, red, green, blue);
+          });
+
+    case 32:
+      return CreateSkiaImageFromDib(
+          this, kBGRA_8888_SkColorType,
+          IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
+
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
+
+bool CFX_DIBBase::IsPremultiplied() const {
+  return false;
+}
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index ef3218d..6bff316 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -5,6 +5,7 @@
 #include "core/fxge/skia/fx_skia_device.h"
 
 #include <math.h>
+#include <stddef.h>
 #include <stdint.h>
 
 #include <algorithm>
@@ -28,9 +29,9 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_coordinates.h"
+#include "core/fxcrt/fx_memory.h"
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/stl_util.h"
-#include "core/fxge/calculate_pitch.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
@@ -46,11 +47,10 @@
 #include "core/fxge/text_char_pos.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkClipOp.h"
@@ -64,14 +64,16 @@
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkPathEffect.h"
 #include "third_party/skia/include/core/SkPathUtils.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/core/SkRSXform.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSamplingOptions.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
+#include "third_party/skia/include/core/SkTileMode.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
@@ -154,66 +156,6 @@
 #endif  // SHOW_SKIA_PATH
 }
 
-bool IsRGBColorGrayScale(uint32_t color) {
-  return FXARGB_R(color) == FXARGB_G(color) &&
-         FXARGB_R(color) == FXARGB_B(color);
-}
-
-// Called by Upsample, return a 32 bit-per-pixel buffer filled with 2 colors
-// from a 1 bit-per-pixel source palette.
-DataVector<uint32_t> Fill32BppDestStorageWith1BppSource(
-    const RetainPtr<CFX_DIBBase>& source) {
-  DCHECK_EQ(1, source->GetBPP());
-  int width = source->GetWidth();
-  int height = source->GetHeight();
-  void* buffer = source->GetBuffer().data();
-  DCHECK(buffer);
-
-  uint32_t color0 = source->GetPaletteArgb(0);
-  uint32_t color1 = source->GetPaletteArgb(1);
-  DataVector<uint32_t> dst32_storage(Fx2DSizeOrDie(width, height));
-  pdfium::span<SkPMColor> dst32_pixels(dst32_storage);
-
-  for (int y = 0; y < height; ++y) {
-    const uint8_t* src_row =
-        static_cast<const uint8_t*>(buffer) + y * source->GetPitch();
-    pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
-    for (int x = 0; x < width; ++x) {
-      bool use_color1 = src_row[x / 8] & (1 << (7 - x % 8));
-      dst_row[x] = use_color1 ? color1 : color0;
-    }
-  }
-  return dst32_storage;
-}
-
-// Called by Upsample(), returns a 32 bit-per-pixel buffer filled with colors
-// from `palette`.
-DataVector<uint32_t> Fill32BppDestStorageWithPalette(
-    const RetainPtr<CFX_DIBBase>& source,
-    pdfium::span<const uint32_t> palette) {
-  DCHECK_EQ(8, source->GetBPP());
-  int width = source->GetWidth();
-  int height = source->GetHeight();
-  void* buffer = source->GetBuffer().data();
-  DCHECK(buffer);
-  DataVector<uint32_t> dst32_storage(Fx2DSizeOrDie(width, height));
-  pdfium::span<SkPMColor> dst32_pixels(dst32_storage);
-
-  for (int y = 0; y < height; ++y) {
-    const uint8_t* src_row =
-        static_cast<const uint8_t*>(buffer) + y * source->GetPitch();
-    pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
-    for (int x = 0; x < width; ++x) {
-      unsigned index = src_row[x];
-      if (index >= palette.size()) {
-        index = 0;
-      }
-      dst_row[x] = palette[index];
-    }
-  }
-  return dst32_storage;
-}
-
 static void DebugValidate(const RetainPtr<CFX_DIBitmap>& bitmap,
                           const RetainPtr<CFX_DIBitmap>& device) {
   if (bitmap) {
@@ -331,7 +273,6 @@
     case BlendMode::kLuminosity:
       return SkBlendMode::kLuminosity;
     case BlendMode::kNormal:
-    default:
       return SkBlendMode::kSrcOver;
   }
 }
@@ -668,104 +609,6 @@
   paint->setBlendMode(GetSkiaBlendMode(blend_type));
 }
 
-bool Upsample(const RetainPtr<CFX_DIBBase>& pSource,
-              DataVector<uint32_t>& dst32_storage,
-              SkBitmap* skBitmap,
-              bool forceAlpha) {
-  void* buffer = pSource->GetBuffer().data();
-  if (!buffer)
-    return false;
-  SkColorType colorType = forceAlpha || pSource->IsMaskFormat()
-                              ? SkColorType::kAlpha_8_SkColorType
-                              : SkColorType::kGray_8_SkColorType;
-  SkAlphaType alphaType = kPremul_SkAlphaType;
-  int width = pSource->GetWidth();
-  int height = pSource->GetHeight();
-  int rowBytes = pSource->GetPitch();
-  switch (pSource->GetBPP()) {
-    case 1: {
-      // By default, the two colors for grayscale are 0xFF and 0x00 unless they
-      // are specified in the palette.
-      uint8_t color0 = 0x00;
-      uint8_t color1 = 0xFF;
-
-      if (pSource->GetFormat() == FXDIB_Format::k1bppRgb &&
-          pSource->HasPalette()) {
-        uint32_t palette_color0 = pSource->GetPaletteArgb(0);
-        uint32_t palette_color1 = pSource->GetPaletteArgb(1);
-        bool use_gray_colors = IsRGBColorGrayScale(palette_color0) &&
-                               IsRGBColorGrayScale(palette_color1);
-        if (!use_gray_colors) {
-          dst32_storage = Fill32BppDestStorageWith1BppSource(pSource);
-          rowBytes = width * sizeof(uint32_t);
-          colorType = kBGRA_8888_SkColorType;
-          break;
-        }
-
-        color0 = FXARGB_R(palette_color0);
-        color1 = FXARGB_R(palette_color1);
-      }
-
-      const int src_row_bytes = rowBytes;  // Save original value.
-      rowBytes = fxge::CalculatePitch32OrDie(/*bpp=*/8, width);
-      dst32_storage = DataVector<uint32_t>(Fx2DSizeOrDie(rowBytes / 4, height));
-      pdfium::span<uint8_t> dst8_pixels =
-          pdfium::as_writable_bytes(pdfium::make_span(dst32_storage));
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* src_row =
-            static_cast<const uint8_t*>(buffer) + y * src_row_bytes;
-        pdfium::span<uint8_t> dst_row = dst8_pixels.subspan(y * rowBytes);
-        for (int x = 0; x < width; ++x)
-          dst_row[x] = src_row[x >> 3] & (1 << (~x & 0x07)) ? color1 : color0;
-      }
-      break;
-    }
-    case 8:
-      // we upscale ctables to 32bit.
-      if (pSource->HasPalette()) {
-        const size_t src_palette_size = pSource->GetRequiredPaletteSize();
-        pdfium::span<const uint32_t> src_palette = pSource->GetPaletteSpan();
-        CHECK_LE(src_palette_size, src_palette.size());
-        if (src_palette_size < src_palette.size())
-          src_palette = src_palette.first(src_palette_size);
-
-        dst32_storage = Fill32BppDestStorageWithPalette(pSource, src_palette);
-        rowBytes = width * sizeof(uint32_t);
-        colorType = kBGRA_8888_SkColorType;
-      }
-      break;
-    case 24: {
-      dst32_storage = DataVector<uint32_t>(Fx2DSizeOrDie(width, height));
-      pdfium::span<uint32_t> dst32_pixels(dst32_storage);
-      for (int y = 0; y < height; ++y) {
-        const uint8_t* srcRow =
-            static_cast<const uint8_t*>(buffer) + y * rowBytes;
-        pdfium::span<uint32_t> dst_row = dst32_pixels.subspan(y * width);
-        for (int x = 0; x < width; ++x) {
-          dst_row[x] = SkPackARGB32NoCheck(
-              0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1], srcRow[x * 3 + 0]);
-        }
-      }
-      rowBytes = width * sizeof(uint32_t);
-      colorType = kBGRA_8888_SkColorType;
-      alphaType = kOpaque_SkAlphaType;
-      break;
-    }
-    case 32:
-      colorType = kBGRA_8888_SkColorType;
-      break;
-    default:
-      NOTREACHED();
-  }
-  if (!dst32_storage.empty()) {
-    buffer = dst32_storage.data();
-  }
-  SkImageInfo imageInfo =
-      SkImageInfo::Make(width, height, colorType, alphaType);
-  skBitmap->installPixels(imageInfo, buffer, rowBytes);
-  return true;
-}
-
 // Makes a bitmap filled with a solid color for debugging with `SkPicture`.
 RetainPtr<CFX_DIBitmap> MakeDebugBitmap(int width, int height, uint32_t color) {
   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
@@ -833,10 +676,8 @@
     bool bGroupKnockout)
     : m_pBitmap(std::move(pBitmap)),
       m_pBackdropBitmap(pBackdropBitmap),
-      m_pRecorder(nullptr),
       m_bRgbByteOrder(bRgbByteOrder),
       m_bGroupKnockout(bGroupKnockout) {
-  SkBitmap skBitmap;
   SkColorType color_type;
   const int bpp = m_pBitmap->GetBPP();
   if (bpp == 8) {
@@ -866,14 +707,13 @@
   SkImageInfo imageInfo =
       SkImageInfo::Make(m_pBitmap->GetWidth(), m_pBitmap->GetHeight(),
                         color_type, kPremul_SkAlphaType);
-  skBitmap.installPixels(imageInfo, m_pBitmap->GetBuffer().data(),
-                         m_pBitmap->GetPitch());
-  m_pCanvas = new SkCanvas(skBitmap);
+  surface_ = SkSurfaces::WrapPixels(
+      imageInfo, m_pBitmap->GetWritableBuffer().data(), m_pBitmap->GetPitch());
+  m_pCanvas = surface_->getCanvas();
 }
 
-CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder)
-    : m_pRecorder(recorder), m_bGroupKnockout(false) {
-  m_pCanvas = m_pRecorder->getRecordingCanvas();
+CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkCanvas* canvas)
+    : m_pCanvas(canvas), m_bGroupKnockout(false) {
   int width = m_pCanvas->imageInfo().width();
   int height = m_pCanvas->imageInfo().height();
   DCHECK_EQ(kUnknown_SkColorType, m_pCanvas->imageInfo().colorType());
@@ -897,10 +737,6 @@
                                       height, m_pBitmap, /*src_left=*/0,
                                       /*src_top=*/0);
   }
-
-  if (!m_pRecorder) {
-    delete m_pCanvas;
-  }
 }
 
 bool CFX_SkiaDeviceDriver::DrawDeviceText(
@@ -917,21 +753,6 @@
   if (pFont->GetFontSpan().empty())
     return false;
 
-  // If a glyph's default width is no less than its width defined in the PDF,
-  // draw the glyph with path since it can be scaled to avoid overlapping with
-  // the adjacent glyphs (if there are any). Otherwise, use the device driver
-  // to render the glyph without any adjustments.
-  const CFX_SubstFont* subst_font = pFont->GetSubstFont();
-  const int subst_font_weight =
-      (subst_font && subst_font->IsBuiltInGenericFont()) ? subst_font->m_Weight
-                                                         : 0;
-  for (const TextCharPos& cp : pCharPos) {
-    const int glyph_width = pFont->GetGlyphWidth(
-        cp.m_GlyphIndex, cp.m_FontCharWidth, subst_font_weight);
-    if (cp.m_FontCharWidth <= glyph_width)
-      return false;
-  }
-
   if (TryDrawText(pCharPos, pFont, mtObject2Device, font_size, color,
                   options)) {
     return true;
@@ -1007,6 +828,8 @@
 
 // TODO(crbug.com/pdfium/1999): Merge with `DrawDeviceText()` and refactor
 // common logic.
+// TODO(crbug.com/pdfium/1774): Sometimes the thickness of the glyphs is not
+// ideal. Improve text rendering results regarding different font weight.
 bool CFX_SkiaDeviceDriver::TryDrawText(pdfium::span<const TextCharPos> char_pos,
                                        const CFX_Font* pFont,
                                        const CFX_Matrix& matrix,
@@ -1091,13 +914,26 @@
         glyphs.data(), glyphs.size() * sizeof(uint16_t), m_rsxform.data(), font,
         SkTextEncoding::kGlyphID);
     m_pCanvas->drawTextBlob(blob, 0, 0, skPaint);
-  } else {
-    const DataVector<SkPoint>& positions = m_charDetails.GetPositions();
-    for (size_t i = 0; i < m_charDetails.Count(); ++i) {
-      sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromText(
-          &glyphs[i], sizeof(glyphs[i]), font, SkTextEncoding::kGlyphID);
-      m_pCanvas->drawTextBlob(blob, positions[i].fX, positions[i].fY, skPaint);
+    return true;
+  }
+  const DataVector<SkPoint>& positions = m_charDetails.GetPositions();
+  const DataVector<uint32_t>& widths = m_charDetails.GetFontCharWidths();
+  for (size_t i = 0; i < m_charDetails.Count(); ++i) {
+    const uint32_t font_glyph_width =
+        pFont ? pFont->GetGlyphWidth(glyphs[i]) : 0;
+    const uint32_t pdf_glyph_width = widths[i];
+    if (pdf_glyph_width > 0 && font_glyph_width > 0) {
+      // Scale the glyph from its default width `pdf_glyph_width` to the
+      // targeted width `pdf_glyph_width`.
+      font.setScaleX(scaleX * SkIntToScalar(pdf_glyph_width) /
+                     font_glyph_width);
+    } else {
+      font.setScaleX(scaleX);
     }
+    auto blob =
+        SkTextBlob::MakeFromPosText(&glyphs[i], sizeof(uint16_t), &positions[i],
+                                    font, SkTextEncoding::kGlyphID);
+    m_pCanvas->drawTextBlob(blob, 0, 0, skPaint);
   }
   return true;
 }
@@ -1115,22 +951,17 @@
 }
 
 bool CFX_SkiaDeviceDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
-  if (!mask->IsMaskFormat()) {
-    NOTREACHED();
-    return false;
-  }
+  CHECK(mask->IsMaskFormat());
 
-  // Storage vector must outlive `skia_mask`.
-  DataVector<uint32_t> dst32_storage;
-  SkBitmap skia_mask;
-  if (!Upsample(mask, dst32_storage, &skia_mask, /*forceAlpha=*/true)) {
+  sk_sp<SkImage> skia_mask = mask->RealizeSkImage();
+  if (!skia_mask) {
     return false;
   }
-  skia_mask.setImmutable();
+  DCHECK_EQ(skia_mask->colorType(), kAlpha_8_SkColorType);
 
   SkPaint paint;
   paint.setBlendMode(SkBlendMode::kDstIn);
-  m_pCanvas->drawImageRect(skia_mask.asImage(),
+  m_pCanvas->drawImageRect(skia_mask,
                            SkRect::Make(m_pCanvas->imageInfo().bounds()),
                            SkSamplingOptions(), &paint);
   return true;
@@ -1156,8 +987,7 @@
              FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_ALPHA_OUTPUT |
              FXRC_FILLSTROKE_PATH | FXRC_SHADING;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -1518,35 +1348,28 @@
   if (!m_pBitmap)
     return true;
 
-  uint8_t* srcBuffer = m_pBitmap->GetBuffer().data();
-  if (!srcBuffer)
+  const uint8_t* input_buffer = m_pBitmap->GetBuffer().data();
+  if (!input_buffer) {
     return true;
+  }
 
-  int srcWidth = m_pBitmap->GetWidth();
-  int srcHeight = m_pBitmap->GetHeight();
-  size_t srcRowBytes = m_pBitmap->GetPitch();
-  SkImageInfo srcImageInfo = SkImageInfo::Make(
-      srcWidth, srcHeight, SkColorType::kN32_SkColorType, kPremul_SkAlphaType);
-  SkBitmap skSrcBitmap;
-  skSrcBitmap.installPixels(srcImageInfo, srcBuffer, srcRowBytes);
-  skSrcBitmap.setImmutable();
+  uint8_t* output_buffer = pBitmap->GetWritableBuffer().data();
+  DCHECK(output_buffer);
 
-  uint8_t* dstBuffer = pBitmap->GetBuffer().data();
-  DCHECK(dstBuffer);
+  SkImageInfo input_info =
+      SkImageInfo::Make(m_pBitmap->GetWidth(), m_pBitmap->GetHeight(),
+                        SkColorType::kN32_SkColorType, kPremul_SkAlphaType);
+  sk_sp<SkImage> input = SkImages::RasterFromPixmap(
+      SkPixmap(input_info, input_buffer, m_pBitmap->GetPitch()),
+      /*rasterReleaseProc=*/nullptr, /*releaseContext=*/nullptr);
 
-  int dstWidth = pBitmap->GetWidth();
-  int dstHeight = pBitmap->GetHeight();
-  size_t dstRowBytes = pBitmap->GetPitch();
-  SkImageInfo dstImageInfo = SkImageInfo::Make(
-      dstWidth, dstHeight, Get32BitSkColorType(m_bRgbByteOrder),
-      kPremul_SkAlphaType);
-  SkBitmap skDstBitmap;
-  skDstBitmap.installPixels(dstImageInfo, dstBuffer, dstRowBytes);
+  SkImageInfo output_info = SkImageInfo::Make(
+      pBitmap->GetWidth(), pBitmap->GetHeight(),
+      Get32BitSkColorType(m_bRgbByteOrder), kPremul_SkAlphaType);
+  sk_sp<SkSurface> output =
+      SkSurfaces::WrapPixels(output_info, output_buffer, pBitmap->GetPitch());
 
-  SkCanvas canvas(skDstBitmap);
-  canvas.drawImageRect(skSrcBitmap.asImage(),
-                       SkRect::MakeXYWH(left, top, dstWidth, dstHeight),
-                       SkSamplingOptions(), /*paint=*/nullptr);
+  output->getCanvas()->drawImage(input, left, top, SkSamplingOptions());
   return true;
 }
 
@@ -1620,41 +1443,16 @@
   return false;
 }
 
-void CFX_DIBitmap::PreMultiply() {
-  if (GetBPP() != 32)
-    return;
-
-  void* buffer = GetBuffer().data();
-  if (!buffer)
-    return;
-
-  Format prior_format = m_nFormat;
-  ForcePreMultiply();
-  if (prior_format == Format::kPreMultiplied)
-    return;
-
-  int height = GetHeight();
-  int width = GetWidth();
-  int row_bytes = GetPitch();
-  SkImageInfo unpremultiplied_info =
-      SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType);
-  SkPixmap unpremultiplied(unpremultiplied_info, buffer, row_bytes);
-  SkImageInfo premultiplied_info =
-      SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
-  SkPixmap premultiplied(premultiplied_info, buffer, row_bytes);
-  unpremultiplied.readPixels(premultiplied);
-}
-
 void CFX_DIBitmap::UnPreMultiply() {
   if (GetBPP() != 32)
     return;
 
-  void* buffer = GetBuffer().data();
+  void* buffer = GetWritableBuffer().data();
   if (!buffer)
     return;
 
   Format prior_format = m_nFormat;
-  ForceUnPreMultiply();
+  m_nFormat = Format::kUnPreMultiplied;
   if (prior_format == Format::kUnPreMultiplied)
     return;
 
@@ -1674,8 +1472,8 @@
   m_nFormat = Format::kPreMultiplied;
 }
 
-void CFX_DIBitmap::ForceUnPreMultiply() {
-  m_nFormat = Format::kUnPreMultiplied;
+bool CFX_DIBitmap::IsPremultiplied() const {
+  return m_nFormat == Format::kPreMultiplied;
 }
 
 bool CFX_SkiaDeviceDriver::DrawBitsWithMask(
@@ -1685,17 +1483,19 @@
     const CFX_Matrix& matrix,
     BlendMode blend_type) {
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
-  // Storage vectors must outlive `skBitmap` and `skMask`.
-  DataVector<uint32_t> src32_storage;
-  DataVector<uint32_t> mask32_storage;
-  SkBitmap skBitmap;
-  SkBitmap skMask;
-  if (!Upsample(pSource, src32_storage, &skBitmap, /*forceAlpha=*/false)) {
+
+  sk_sp<SkImage> skia_source = pSource->RealizeSkImage();
+  if (!skia_source) {
     return false;
   }
-  if (!Upsample(pMask, mask32_storage, &skMask, /*forceAlpha=*/true)) {
+
+  DCHECK(pMask->IsMaskFormat());
+  sk_sp<SkImage> skia_mask = pMask->RealizeSkImage();
+  if (!skia_mask) {
     return false;
   }
+  DCHECK_EQ(skia_mask->colorType(), kAlpha_8_SkColorType);
+
   {
     SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
 
@@ -1707,17 +1507,17 @@
     SkPaint paint;
     SetBitmapPaintForMerge(pSource->IsMaskFormat(), !m_FillOptions.aliased_path,
                            0xFFFFFFFF, bitmap_alpha, blend_type, &paint);
-    sk_sp<SkImage> skSrc = SkImages::RasterFromBitmap(skBitmap);
-    sk_sp<SkShader> skSrcShader = skSrc->makeShader(
+    sk_sp<SkShader> source_shader = skia_source->makeShader(
         SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
-    sk_sp<SkImage> skMaskImage = SkImages::RasterFromBitmap(skMask);
-    sk_sp<SkShader> skMaskShader = skMaskImage->makeShader(
+    sk_sp<SkShader> mask_shader = skia_mask->makeShader(
         SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
-    paint.setShader(
-        SkShaders::Blend(SkBlendMode::kSrcIn, skMaskShader, skSrcShader));
-    SkRect r = {0, 0, SkIntToScalar(src_width), SkIntToScalar(src_height)};
-    m_pCanvas->drawRect(r, paint);
+    paint.setShader(SkShaders::Blend(
+        SkBlendMode::kSrcIn, std::move(mask_shader), std::move(source_shader)));
+    m_pCanvas->drawRect(
+        SkRect::MakeWH(SkIntToScalar(src_width), SkIntToScalar(src_height)),
+        paint);
   }
+
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
   return true;
 }
@@ -1755,13 +1555,10 @@
     BlendMode blend_type) {
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
 
-  // Storage vector must outlive `skBitmap`.
-  DataVector<uint32_t> dst32_storage;
-  SkBitmap skBitmap;
-  if (!Upsample(pSource, dst32_storage, &skBitmap, /*forceAlpha=*/false)) {
+  sk_sp<SkImage> skia_source = pSource->RealizeSkImage();
+  if (!skia_source) {
     return false;
   }
-  skBitmap.setImmutable();
 
   {
     SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
@@ -1793,12 +1590,13 @@
     }
 
     m_pCanvas->drawImageRect(
-        skBitmap.asImage(),
+        skia_source,
         SkRect::MakeLTRB(src_rect.left, src_rect.top, src_rect.right,
                          src_rect.bottom),
         SkRect::MakeWH(src_rect.Width(), src_rect.Height()), sampling_options,
         &paint, SkCanvas::kFast_SrcRectConstraint);
   }
+
   DebugValidate(m_pBitmap, m_pBackdropBitmap);
   return true;
 }
@@ -1810,15 +1608,6 @@
   static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver())->Clear(color);
 }
 
-std::unique_ptr<SkPictureRecorder> CFX_DefaultRenderDevice::CreateRecorder(
-    const SkRect& bounds) {
-  auto recorder = std::make_unique<SkPictureRecorder>();
-  recorder->beginRecording(bounds);
-
-  SetDeviceDriver(std::make_unique<CFX_SkiaDeviceDriver>(recorder.get()));
-  return recorder;
-}
-
 bool CFX_DefaultRenderDevice::AttachSkiaImpl(
     RetainPtr<CFX_DIBitmap> pBitmap,
     bool bRgbByteOrder,
@@ -1837,10 +1626,11 @@
   return true;
 }
 
-bool CFX_DefaultRenderDevice::AttachRecorder(SkPictureRecorder* recorder) {
-  if (!recorder)
+bool CFX_DefaultRenderDevice::AttachCanvas(SkCanvas* canvas) {
+  if (!canvas) {
     return false;
-  SetDeviceDriver(std::make_unique<CFX_SkiaDeviceDriver>(recorder));
+  }
+  SetDeviceDriver(std::make_unique<CFX_SkiaDeviceDriver>(canvas));
   return true;
 }
 
@@ -1862,14 +1652,3 @@
   SetDeviceDriver(std::move(driver));
   return true;
 }
-
-bool CFX_DefaultRenderDevice::SetBitsWithMask(
-    const RetainPtr<CFX_DIBBase>& pBitmap,
-    const RetainPtr<CFX_DIBBase>& pMask,
-    int left,
-    int top,
-    int bitmap_alpha,
-    BlendMode blend_type) {
-  return static_cast<CFX_SkiaDeviceDriver*>(GetDeviceDriver())
-      ->SetBitsWithMask(pBitmap, pMask, left, top, bitmap_alpha, blend_type);
-}
diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h
index beed9a3..3285f8f 100644
--- a/core/fxge/skia/fx_skia_device.h
+++ b/core/fxge/skia/fx_skia_device.h
@@ -12,18 +12,20 @@
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_path.h"
 #include "core/fxge/renderdevicedriver_iface.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/skia/include/core/SkPoint.h"
 #include "third_party/skia/include/core/SkRSXform.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
 
 class CFX_Font;
 class CFX_Matrix;
 class SkCanvas;
-class SkPictureRecorder;
+class SkSurface;
 class TextCharPos;
 struct CFX_TextRenderOptions;
 
@@ -39,7 +41,7 @@
       RetainPtr<CFX_DIBitmap> pBackdropBitmap,
       bool bGroupKnockout);
 
-  explicit CFX_SkiaDeviceDriver(SkPictureRecorder* recorder);
+  explicit CFX_SkiaDeviceDriver(SkCanvas* canvas);
   ~CFX_SkiaDeviceDriver() override;
 
   /** Options */
@@ -217,8 +219,8 @@
   // bitmap is 24 bpp and cannot be directly used as the back of a SkCanvas.
   RetainPtr<CFX_DIBitmap> m_pOriginalBitmap;
 
-  SkCanvas* m_pCanvas;
-  SkPictureRecorder* const m_pRecorder;
+  sk_sp<SkSurface> surface_;
+  UnownedPtr<SkCanvas> m_pCanvas;
   CFX_FillRenderOptions m_FillOptions;
   bool m_bRgbByteOrder;
   bool m_bGroupKnockout;
diff --git a/core/fxge/skia/fx_skia_device_embeddertest.cpp b/core/fxge/skia/fx_skia_device_embeddertest.cpp
index 517d863..ca38a1e 100644
--- a/core/fxge/skia/fx_skia_device_embeddertest.cpp
+++ b/core/fxge/skia/fx_skia_device_embeddertest.cpp
@@ -5,7 +5,12 @@
 #include "core/fxge/skia/fx_skia_device.h"
 
 #include <memory>
+#include <set>
+#include <utility>
 
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/render/cpdf_pagerendercontext.h"
+#include "core/fxcrt/fx_codepage.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_font.h"
@@ -13,15 +18,26 @@
 #include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/cfx_textrenderoptions.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/text_char_pos.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "fpdfsdk/cpdfsdk_renderpage.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdfview.h"
+#include "testing/embedder_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
 
 namespace {
 
+using ::testing::NiceMock;
+using ::testing::SizeIs;
+using ::testing::WithArg;
+
 struct State {
   enum class Change { kNo, kYes };
   enum class Save { kNo, kYes };
@@ -44,11 +60,14 @@
 void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) {
   TextCharPos charPos[1];
   charPos[0].m_Origin = CFX_PointF(0, 1);
-  charPos[0].m_GlyphIndex = 1;
+  charPos[0].m_GlyphIndex = 0;
   charPos[0].m_FontCharWidth = 4;
 
   CFX_Font font;
-  float fontSize = 1;
+  font.LoadSubst("Courier", /*bTrueType=*/true, /*flags=*/0,
+                 /*weight=*/400, /*italic_angle=*/0, FX_CodePage::kShiftJIS,
+                 /*bVertical=*/false);
+  float fontSize = 20;
   CFX_Path clipPath;
   CFX_Path clipPath2;
   clipPath.AppendRect(0, 0, 3, 1);
@@ -63,7 +82,10 @@
   CFX_Matrix matrix2;
   matrix2.Translate(1, 0);
   CFX_GraphStateData graphState;
-  static constexpr CFX_TextRenderOptions kTextOptions;
+  // Turn off anti-aliasing so that pixels with transitional colors can be
+  // avoided.
+  static constexpr CFX_TextRenderOptions kTextOptions(
+      CFX_TextRenderOptions::kAliasing);
   if (state.m_save == State::Save::kYes)
     driver->SaveState();
   if (state.m_clip != State::Clip::kNo)
@@ -141,6 +163,47 @@
   EXPECT_EQ(state.m_pixel, pixel);
 }
 
+void RenderPageToSkCanvas(FPDF_PAGE page,
+                          int start_x,
+                          int start_y,
+                          int size_x,
+                          int size_y,
+                          SkCanvas* canvas) {
+  CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page);
+
+  auto context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* unowned_context = context.get();
+
+  CPDF_Page::RenderContextClearer clearer(cpdf_page);
+  cpdf_page->SetRenderContext(std::move(context));
+
+  auto default_device = std::make_unique<CFX_DefaultRenderDevice>();
+  default_device->AttachCanvas(canvas);
+  unowned_context->m_pDevice = std::move(default_device);
+
+  CPDFSDK_RenderPageWithContext(unowned_context, cpdf_page, start_x, start_y,
+                                size_x, size_y, /*rotate=*/0, /*flags=*/0,
+                                /*color_scheme=*/nullptr,
+                                /*need_to_restore=*/true, /*pause=*/nullptr);
+}
+
+class MockCanvas : public SkNoDrawCanvas {
+ public:
+  MockCanvas(int width, int height) : SkNoDrawCanvas(width, height) {}
+
+  MOCK_METHOD(void,
+              onDrawImageRect2,
+              (const SkImage*,
+               const SkRect&,
+               const SkRect&,
+               const SkSamplingOptions&,
+               const SkPaint*,
+               SrcRectConstraint),
+              (override));
+};
+
+using FxgeSkiaEmbedderTest = EmbedderTest;
+
 }  // namespace
 
 TEST(fxge, SkiaStateEmpty) {
@@ -165,8 +228,7 @@
                         State::Graphic::kPath, 0xFF112233});
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-TEST(fxge, DISABLED_SkiaStateText) {
+TEST(fxge, SkiaStateText) {
   if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
     return;
 
@@ -182,3 +244,48 @@
     return;
   Harness(&OutOfSequenceClipTest, {});
 }
+
+TEST_F(FxgeSkiaEmbedderTest, RenderBigImageTwice) {
+  static constexpr int kImageWidth = 5100;
+  static constexpr int kImageHeight = 6600;
+
+  // Page size that renders 20 image pixels per output pixel. This value evenly
+  // divides both the image width and half the image height.
+  static constexpr int kPageToImageFactor = 20;
+  static constexpr int kPageWidth = kImageWidth / kPageToImageFactor;
+  static constexpr int kPageHeight = kImageHeight / kPageToImageFactor;
+
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    GTEST_SKIP() << "Skia is not the default renderer";
+  }
+
+  ASSERT_TRUE(OpenDocument("bug_2034.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  std::set<int> image_ids;
+  NiceMock<MockCanvas> canvas(kPageWidth, kPageHeight / 2);
+  EXPECT_CALL(canvas, onDrawImageRect2)
+      .WillRepeatedly(WithArg<0>([&image_ids](const SkImage* image) {
+        ASSERT_TRUE(image);
+        image_ids.insert(image->uniqueID());
+
+        // TODO(crbug.com/pdfium/2026): Image dimensions should be clipped to
+        // 5100x3320. The extra `kPageToImageFactor` accounts for anti-aliasing.
+        EXPECT_EQ(SkISize::Make(kImageWidth, kImageHeight), image->dimensions())
+            << "Actual image dimensions: " << image->width() << "x"
+            << image->height();
+      }));
+
+  // Render top half.
+  RenderPageToSkCanvas(page, /*start_x=*/0, /*start_y=*/0,
+                       /*size_x=*/kPageWidth, /*size_y=*/kPageHeight, &canvas);
+
+  // Render bottom half.
+  RenderPageToSkCanvas(page, /*start_x=*/0, /*start_y=*/-kPageHeight / 2,
+                       /*size_x=*/kPageWidth, /*size_y=*/kPageHeight, &canvas);
+
+  EXPECT_THAT(image_ids, SizeIs(1));
+
+  UnloadPage(page);
+}
diff --git a/core/fxge/systemfontinfo_iface.h b/core/fxge/systemfontinfo_iface.h
index 3e661b8..90cdb19 100644
--- a/core/fxge/systemfontinfo_iface.h
+++ b/core/fxge/systemfontinfo_iface.h
@@ -13,7 +13,7 @@
 #include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_codepage_forward.h"
 #include "core/fxge/cfx_fontmapper.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 constexpr uint32_t kTableNAME = CFX_FontMapper::MakeTag('n', 'a', 'm', 'e');
 constexpr uint32_t kTableTTCF = CFX_FontMapper::MakeTag('t', 't', 'c', 'f');
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index ae168f4..c2da036 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -29,7 +29,7 @@
 #include "core/fxge/cfx_glyphcache.h"
 #include "core/fxge/cfx_path.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "core/fxge/dib/cfx_dibextractor.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/dib/fx_dib.h"
 #include "core/fxge/freetype/fx_freetype.h"
@@ -555,10 +555,7 @@
     WriteStream(buf);
     WritePSBinary(compress_result.data);
   } else {
-    CFX_DIBExtractor source_extractor(pSource);
-    RetainPtr<CFX_DIBBase> pConverted = source_extractor.GetBitmap();
-    if (!pConverted)
-      return false;
+    RetainPtr<CFX_DIBBase> pConverted = pSource;
     switch (pSource->GetFormat()) {
       case FXDIB_Format::k1bppRgb:
       case FXDIB_Format::kRgb32:
diff --git a/core/fxge/win32/cfx_psrenderer.h b/core/fxge/win32/cfx_psrenderer.h
index b8010f6..ad914be 100644
--- a/core/fxge/win32/cfx_psrenderer.h
+++ b/core/fxge/win32/cfx_psrenderer.h
@@ -24,7 +24,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_DIBBase;
 class CFX_Font;
diff --git a/core/fxge/win32/cfx_psrenderer_unittest.cpp b/core/fxge/win32/cfx_psrenderer_unittest.cpp
index 6186f0f..9927692 100644
--- a/core/fxge/win32/cfx_psrenderer_unittest.cpp
+++ b/core/fxge/win32/cfx_psrenderer_unittest.cpp
@@ -14,7 +14,7 @@
 #include "core/fxge/win32/cfx_psfonttracker.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace {
 
diff --git a/core/fxge/win32/cgdi_device_driver.cpp b/core/fxge/win32/cgdi_device_driver.cpp
index 048377d..6ae582e 100644
--- a/core/fxge/win32/cgdi_device_driver.cpp
+++ b/core/fxge/win32/cgdi_device_driver.cpp
@@ -18,6 +18,7 @@
 #include "core/fxge/cfx_fillrenderoptions.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/render_defines.h"
 #include "core/fxge/win32/cwin32_platform.h"
@@ -152,10 +153,11 @@
   EndPath(hDC);
 }
 
-ByteString GetBitmapInfo(const RetainPtr<CFX_DIBitmap>& pBitmap) {
+ByteString GetBitmapInfo(const RetainPtr<CFX_DIBBase>& source) {
   int len = sizeof(BITMAPINFOHEADER);
-  if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8)
-    len += sizeof(DWORD) * (int)(1 << pBitmap->GetBPP());
+  if (source->GetBPP() == 1 || source->GetBPP() == 8) {
+    len += sizeof(DWORD) * (int)(1 << source->GetBPP());
+  }
 
   ByteString result;
   {
@@ -164,30 +166,33 @@
     BITMAPINFOHEADER* pbmih = reinterpret_cast<BITMAPINFOHEADER*>(cspan.data());
     memset(pbmih, 0, sizeof(BITMAPINFOHEADER));
     pbmih->biSize = sizeof(BITMAPINFOHEADER);
-    pbmih->biBitCount = pBitmap->GetBPP();
+    pbmih->biBitCount = source->GetBPP();
     pbmih->biCompression = BI_RGB;
-    pbmih->biHeight = -(int)pBitmap->GetHeight();
+    pbmih->biHeight = -(int)source->GetHeight();
     pbmih->biPlanes = 1;
-    pbmih->biWidth = pBitmap->GetWidth();
-    if (pBitmap->GetBPP() == 8) {
-      uint32_t* pPalette = (uint32_t*)(pbmih + 1);
-      if (pBitmap->HasPalette()) {
-        pdfium::span<const uint32_t> palette = pBitmap->GetPaletteSpan();
-        for (int i = 0; i < 256; i++)
-          pPalette[i] = palette[i];
+    pbmih->biWidth = source->GetWidth();
+    if (source->GetBPP() == 8) {
+      uint32_t* palette = (uint32_t*)(pbmih + 1);
+      if (source->HasPalette()) {
+        pdfium::span<const uint32_t> palette_span = source->GetPaletteSpan();
+        for (int i = 0; i < 256; i++) {
+          palette[i] = palette_span[i];
+        }
       } else {
-        for (int i = 0; i < 256; i++)
-          pPalette[i] = ArgbEncode(0, i, i, i);
+        for (int i = 0; i < 256; i++) {
+          palette[i] = ArgbEncode(0, i, i, i);
+        }
       }
     }
-    if (pBitmap->GetBPP() == 1) {
-      uint32_t* pPalette = (uint32_t*)(pbmih + 1);
-      if (pBitmap->HasPalette()) {
-        pPalette[0] = pBitmap->GetPaletteSpan()[0];
-        pPalette[1] = pBitmap->GetPaletteSpan()[1];
+    if (source->GetBPP() == 1) {
+      uint32_t* palette = (uint32_t*)(pbmih + 1);
+      if (source->HasPalette()) {
+        pdfium::span<const uint32_t> palette_span = source->GetPaletteSpan();
+        palette[0] = palette_span[0];
+        palette[1] = palette_span[1];
       } else {
-        pPalette[0] = 0;
-        pPalette[1] = 0xffffff;
+        palette[0] = 0;
+        palette[1] = 0xffffff;
       }
     }
   }
@@ -384,85 +389,85 @@
     SaveDC(m_hDC);
 }
 
-bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap1,
+bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr<CFX_DIBBase>& source,
                                      const FX_RECT& src_rect,
                                      int left,
                                      int top) {
   if (m_DeviceType == DeviceType::kPrinter) {
-    RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1->FlipImage(false, true);
-    if (!pBitmap)
+    RetainPtr<CFX_DIBBase> flipped_source = source->FlipImage(false, true);
+    if (!flipped_source) {
       return false;
+    }
 
-    LPBYTE pBuffer = pBitmap->GetBuffer().data();
-    ByteString info = GetBitmapInfo(pBitmap);
+    ByteString info = GetBitmapInfo(flipped_source);
     ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1;
     FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height());
-    dst_rect.Intersect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
+    dst_rect.Intersect(0, 0, flipped_source->GetWidth(),
+                       flipped_source->GetHeight());
     int dst_width = dst_rect.Width();
     int dst_height = dst_rect.Height();
     ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width,
-                    dst_height, pBuffer, (BITMAPINFO*)info.c_str(),
-                    DIB_RGB_COLORS, SRCCOPY);
+                    dst_height, flipped_source->GetBuffer().data(),
+                    (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS, SRCCOPY);
     return true;
   }
 
-  RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
-  LPBYTE pBuffer = pBitmap->GetBuffer().data();
-  ByteString info = GetBitmapInfo(pBitmap);
+  ByteString info = GetBitmapInfo(source);
   ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(),
-                      src_rect.left, pBitmap->GetHeight() - src_rect.bottom, 0,
-                      pBitmap->GetHeight(), pBuffer, (BITMAPINFO*)info.c_str(),
-                      DIB_RGB_COLORS);
+                      src_rect.left, source->GetHeight() - src_rect.bottom, 0,
+                      source->GetHeight(), source->GetBuffer().data(),
+                      (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
   return true;
 }
 
-bool CGdiDeviceDriver::GDI_StretchDIBits(
-    const RetainPtr<CFX_DIBitmap>& pBitmap1,
-    int dest_left,
-    int dest_top,
-    int dest_width,
-    int dest_height,
-    const FXDIB_ResampleOptions& options) {
-  RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
-  if (!pBitmap || dest_width == 0 || dest_height == 0)
+bool CGdiDeviceDriver::GDI_StretchDIBits(const RetainPtr<CFX_DIBBase>& source,
+                                         int dest_left,
+                                         int dest_top,
+                                         int dest_width,
+                                         int dest_height,
+                                         const FXDIB_ResampleOptions& options) {
+  if (!source || dest_width == 0 || dest_height == 0) {
     return false;
+  }
 
-  ByteString info = GetBitmapInfo(pBitmap);
+  ByteString info = GetBitmapInfo(source);
   if ((int64_t)abs(dest_width) * abs(dest_height) <
-          (int64_t)pBitmap1->GetWidth() * pBitmap1->GetHeight() * 4 ||
+          (int64_t)source->GetWidth() * source->GetHeight() * 4 ||
       options.bInterpolateBilinear) {
     SetStretchBltMode(m_hDC, HALFTONE);
   } else {
     SetStretchBltMode(m_hDC, COLORONCOLOR);
   }
-  RetainPtr<CFX_DIBitmap> pToStrechBitmap = pBitmap;
+  RetainPtr<CFX_DIBBase> stretch_source = source;
   if (m_DeviceType == DeviceType::kPrinter &&
-      ((int64_t)pBitmap->GetWidth() * pBitmap->GetHeight() >
+      ((int64_t)source->GetWidth() * source->GetHeight() >
        (int64_t)abs(dest_width) * abs(dest_height))) {
-    pToStrechBitmap = pBitmap->StretchTo(dest_width, dest_height,
-                                         FXDIB_ResampleOptions(), nullptr);
+    stretch_source = source->StretchTo(dest_width, dest_height,
+                                       FXDIB_ResampleOptions(), nullptr);
+    if (!stretch_source) {
+      return false;
+    }
+    info = GetBitmapInfo(stretch_source);
   }
-  ByteString toStrechBitmapInfo = GetBitmapInfo(pToStrechBitmap);
   ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
-                  pToStrechBitmap->GetWidth(), pToStrechBitmap->GetHeight(),
-                  pToStrechBitmap->GetBuffer().data(),
-                  (BITMAPINFO*)toStrechBitmapInfo.c_str(), DIB_RGB_COLORS,
-                  SRCCOPY);
+                  stretch_source->GetWidth(), stretch_source->GetHeight(),
+                  stretch_source->GetBuffer().data(), (BITMAPINFO*)info.c_str(),
+                  DIB_RGB_COLORS, SRCCOPY);
   return true;
 }
 
-bool CGdiDeviceDriver::GDI_StretchBitMask(
-    const RetainPtr<CFX_DIBitmap>& pBitmap1,
-    int dest_left,
-    int dest_top,
-    int dest_width,
-    int dest_height,
-    uint32_t bitmap_color) {
-  RetainPtr<CFX_DIBitmap> pBitmap = pBitmap1;
-  if (!pBitmap || dest_width == 0 || dest_height == 0)
+bool CGdiDeviceDriver::GDI_StretchBitMask(const RetainPtr<CFX_DIBBase>& source,
+                                          int dest_left,
+                                          int dest_top,
+                                          int dest_width,
+                                          int dest_height,
+                                          uint32_t bitmap_color) {
+  if (!source || dest_width == 0 || dest_height == 0) {
     return false;
+  }
 
-  int width = pBitmap->GetWidth(), height = pBitmap->GetHeight();
+  int width = source->GetWidth();
+  int height = source->GetHeight();
   struct {
     BITMAPINFOHEADER bmiHeader;
     uint32_t bmiColors[2];
@@ -502,7 +507,7 @@
   // 0xB8074A
 
   ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
-                  width, height, pBitmap->GetBuffer().data(), (BITMAPINFO*)&bmi,
+                  width, height, source->GetBuffer().data(), (BITMAPINFO*)&bmi,
                   DIB_RGB_COLORS, 0xB8074A);
 
   SelectObject(m_hDC, hOld);
diff --git a/core/fxge/win32/cgdi_device_driver.h b/core/fxge/win32/cgdi_device_driver.h
index 59f24c0..de8c41e 100644
--- a/core/fxge/win32/cgdi_device_driver.h
+++ b/core/fxge/win32/cgdi_device_driver.h
@@ -13,6 +13,8 @@
 #include "core/fxge/renderdevicedriver_iface.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+class CFX_DIBBase;
+
 class CGdiDeviceDriver : public RenderDeviceDriverIface {
  protected:
   CGdiDeviceDriver(HDC hDC, DeviceType device_type);
@@ -50,17 +52,17 @@
 
   void DrawLine(float x1, float y1, float x2, float y2);
 
-  bool GDI_SetDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+  bool GDI_SetDIBits(const RetainPtr<CFX_DIBBase>& source,
                      const FX_RECT& src_rect,
                      int left,
                      int top);
-  bool GDI_StretchDIBits(const RetainPtr<CFX_DIBitmap>& pBitmap,
+  bool GDI_StretchDIBits(const RetainPtr<CFX_DIBBase>& source,
                          int dest_left,
                          int dest_top,
                          int dest_width,
                          int dest_height,
                          const FXDIB_ResampleOptions& options);
-  bool GDI_StretchBitMask(const RetainPtr<CFX_DIBitmap>& pBitmap,
+  bool GDI_StretchBitMask(const RetainPtr<CFX_DIBBase>& source,
                           int dest_left,
                           int dest_top,
                           int dest_width,
diff --git a/core/fxge/win32/cgdi_display_driver.cpp b/core/fxge/win32/cgdi_display_driver.cpp
index 7ba2d76..763e300 100644
--- a/core/fxge/win32/cgdi_display_driver.cpp
+++ b/core/fxge/win32/cgdi_display_driver.cpp
@@ -8,7 +8,7 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_system.h"
-#include "core/fxge/dib/cfx_dibextractor.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/render_defines.h"
 #include "core/fxge/win32/cwin32_platform.h"
@@ -51,14 +51,15 @@
   bmi.bmiHeader.biPlanes = 1;
   bmi.bmiHeader.biWidth = width;
   if (pBitmap->GetBPP() > 8) {
-    ret = ::GetDIBits(hDCMemory, hbmp, 0, height, pBitmap->GetBuffer().data(),
-                      &bmi, DIB_RGB_COLORS) == height;
+    ret = ::GetDIBits(hDCMemory, hbmp, 0, height,
+                      pBitmap->GetWritableBuffer().data(), &bmi,
+                      DIB_RGB_COLORS) == height;
   } else {
     auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
     if (bitmap->Create(width, height, FXDIB_Format::kRgb)) {
       bmi.bmiHeader.biBitCount = 24;
-      ::GetDIBits(hDCMemory, hbmp, 0, height, bitmap->GetBuffer().data(), &bmi,
-                  DIB_RGB_COLORS);
+      ::GetDIBits(hDCMemory, hbmp, 0, height,
+                  bitmap->GetWritableBuffer().data(), &bmi, DIB_RGB_COLORS);
       ret = pBitmap->TransferBitmap(0, 0, width, height, bitmap, 0, 0);
     } else {
       ret = false;
@@ -114,11 +115,7 @@
     FX_RECT alpha_src_rect(0, 0, width, height);
     return SetDIBits(bitmap, 0, alpha_src_rect, left, top, BlendMode::kNormal);
   }
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-  return GDI_SetDIBits(pBitmap, src_rect, left, top);
+  return GDI_SetDIBits(pSource, src_rect, left, top);
 }
 
 bool CGdiDisplayDriver::UseFoxitStretchEngine(
@@ -138,7 +135,7 @@
     dest_top += dest_height;
 
   bitmap_clip.Offset(-dest_left, -dest_top);
-  RetainPtr<CFX_DIBitmap> pStretched =
+  RetainPtr<CFX_DIBBase> pStretched =
       pSource->StretchTo(dest_width, dest_height, options, &bitmap_clip);
   if (!pStretched)
     return true;
@@ -198,23 +195,15 @@
     auto* pPlatform =
         static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
     if (pPlatform->m_GdiplusExt.IsAvailable()) {
-      CFX_DIBExtractor temp(pSource);
-      RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-      if (!pBitmap)
-        return false;
       return pPlatform->m_GdiplusExt.StretchDIBits(
-          m_hDC, pBitmap, dest_left, dest_top, dest_width, dest_height,
+          m_hDC, pSource, dest_left, dest_top, dest_width, dest_height,
           pClipRect, FXDIB_ResampleOptions());
     }
     return UseFoxitStretchEngine(pSource, color, dest_left, dest_top,
                                  dest_width, dest_height, pClipRect,
                                  FXDIB_ResampleOptions());
   }
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-  return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width,
+  return GDI_StretchDIBits(pSource, dest_left, dest_top, dest_width,
                            dest_height, FXDIB_ResampleOptions());
 }
 
diff --git a/core/fxge/win32/cgdi_plus_ext.cpp b/core/fxge/win32/cgdi_plus_ext.cpp
index 06571da..42cf505 100644
--- a/core/fxge/win32/cgdi_plus_ext.cpp
+++ b/core/fxge/win32/cgdi_plus_ext.cpp
@@ -23,11 +23,12 @@
 #include "core/fxge/cfx_gemodule.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/win32/cwin32_platform.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 // Has to come before gdiplus.h
 namespace Gdiplus {
@@ -203,7 +204,7 @@
 }
 
 void OutputImage(Gdiplus::GpGraphics* pGraphics,
-                 const RetainPtr<CFX_DIBitmap>& pBitmap,
+                 const RetainPtr<CFX_DIBBase>& source,
                  const FX_RECT& src_rect,
                  int dest_left,
                  int dest_top,
@@ -212,22 +213,27 @@
   int src_width = src_rect.Width();
   int src_height = src_rect.Height();
   const CGdiplusExt& GdiplusExt = GetGdiplusExt();
-  if (pBitmap->GetBPP() == 1 && (src_rect.left % 8)) {
+  if (source->GetBPP() == 1 && (src_rect.left % 8)) {
     FX_RECT new_rect(0, 0, src_width, src_height);
-    RetainPtr<CFX_DIBitmap> pCloned = pBitmap->ClipTo(src_rect);
+    RetainPtr<CFX_DIBBase> pCloned = source->ClipTo(src_rect);
     if (!pCloned)
       return;
     OutputImage(pGraphics, pCloned, new_rect, dest_left, dest_top, dest_width,
                 dest_height);
     return;
   }
-  int src_pitch = pBitmap->GetPitch();
-  uint8_t* scan0 = pBitmap->GetBuffer()
-                       .subspan(src_rect.top * src_pitch +
-                                pBitmap->GetBPP() * src_rect.left / 8)
-                       .data();
+  int src_pitch = source->GetPitch();
+
+  // `GdipCreateBitmapFromScan0()` requires a `BYTE*` scanline buffer, but in
+  // this case, the bitmap only gets read by `GdipDrawImagePointsI()`, then
+  // disposed of, so it's safe to cast away `const` here.
+  uint8_t* scan0 =
+      const_cast<uint8_t*>(source->GetBuffer()
+                               .subspan(src_rect.top * src_pitch +
+                                        source->GetBPP() * src_rect.left / 8)
+                               .data());
   Gdiplus::GpBitmap* bitmap = nullptr;
-  switch (pBitmap->GetFormat()) {
+  switch (source->GetFormat()) {
     case FXDIB_Format::kArgb:
       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
                                           PixelFormat32bppARGB, scan0, &bitmap);
@@ -248,7 +254,7 @@
       pal[0] = 0;
       pal[1] = 256;
       for (int i = 0; i < 256; i++)
-        pal[i + 2] = pBitmap->GetPaletteArgb(i);
+        pal[i + 2] = source->GetPaletteArgb(i);
       CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
       break;
     }
@@ -575,7 +581,7 @@
 }
 
 bool CGdiplusExt::StretchDIBits(HDC hDC,
-                                const RetainPtr<CFX_DIBitmap>& pBitmap,
+                                const RetainPtr<CFX_DIBBase>& source,
                                 int dest_left,
                                 int dest_top,
                                 int dest_width,
@@ -589,16 +595,16 @@
   if (options.bNoSmoothing) {
     CallFunc(GdipSetInterpolationMode)(
         pGraphics, Gdiplus::InterpolationModeNearestNeighbor);
-  } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
-             pBitmap->GetHeight() > abs(dest_height) / 2) {
+  } else if (source->GetWidth() > abs(dest_width) / 2 ||
+             source->GetHeight() > abs(dest_height) / 2) {
     CallFunc(GdipSetInterpolationMode)(pGraphics,
                                        Gdiplus::InterpolationModeHighQuality);
   } else {
     CallFunc(GdipSetInterpolationMode)(pGraphics,
                                        Gdiplus::InterpolationModeBilinear);
   }
-  FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
-  OutputImage(pGraphics, pBitmap, src_rect, dest_left, dest_top, dest_width,
+  FX_RECT src_rect(0, 0, source->GetWidth(), source->GetHeight());
+  OutputImage(pGraphics, source, src_rect, dest_left, dest_top, dest_width,
               dest_height);
   CallFunc(GdipDeleteGraphics)(pGraphics);
   CallFunc(GdipDeleteGraphics)(pGraphics);
diff --git a/core/fxge/win32/cgdi_plus_ext.h b/core/fxge/win32/cgdi_plus_ext.h
index ff6f165..ced36c9 100644
--- a/core/fxge/win32/cgdi_plus_ext.h
+++ b/core/fxge/win32/cgdi_plus_ext.h
@@ -14,7 +14,7 @@
 
 #include "core/fxcrt/retain_ptr.h"
 
-class CFX_DIBitmap;
+class CFX_DIBBase;
 class CFX_GraphStateData;
 class CFX_Matrix;
 class CFX_Path;
@@ -30,7 +30,7 @@
   void Load();
   bool IsAvailable() { return !!m_hModule; }
   bool StretchDIBits(HDC hDC,
-                     const RetainPtr<CFX_DIBitmap>& pBitmap,
+                     const RetainPtr<CFX_DIBBase>& source,
                      int dest_left,
                      int dest_top,
                      int dest_width,
diff --git a/core/fxge/win32/cgdi_printer_driver.cpp b/core/fxge/win32/cgdi_printer_driver.cpp
index 8286685..28bc516 100644
--- a/core/fxge/win32/cgdi_printer_driver.cpp
+++ b/core/fxge/win32/cgdi_printer_driver.cpp
@@ -17,7 +17,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_font.h"
 #include "core/fxge/cfx_windowsrenderdevice.h"
-#include "core/fxge/dib/cfx_dibextractor.h"
+#include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/render_defines.h"
 #include "core/fxge/text_char_pos.h"
@@ -59,12 +59,7 @@
   if (pSource->IsAlphaFormat())
     return false;
 
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-
-  return GDI_SetDIBits(pBitmap, src_rect, left, top);
+  return GDI_SetDIBits(pSource, src_rect, left, top);
 }
 
 bool CGdiPrinterDriver::StretchDIBits(const RetainPtr<CFX_DIBBase>& pSource,
@@ -82,7 +77,7 @@
       return false;
 
     if (dest_width < 0 || dest_height < 0) {
-      RetainPtr<CFX_DIBitmap> pFlipped =
+      RetainPtr<CFX_DIBBase> pFlipped =
           pSource->FlipImage(dest_width < 0, dest_height < 0);
       if (!pFlipped)
         return false;
@@ -96,11 +91,7 @@
                                 abs(dest_height), color);
     }
 
-    CFX_DIBExtractor temp(pSource);
-    RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-    if (!pBitmap)
-      return false;
-    return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width,
+    return GDI_StretchBitMask(pSource, dest_left, dest_top, dest_width,
                               dest_height, color);
   }
 
@@ -108,7 +99,7 @@
     return false;
 
   if (dest_width < 0 || dest_height < 0) {
-    RetainPtr<CFX_DIBitmap> pFlipped =
+    RetainPtr<CFX_DIBBase> pFlipped =
         pSource->FlipImage(dest_width < 0, dest_height < 0);
     if (!pFlipped)
       return false;
@@ -122,11 +113,7 @@
                              abs(dest_height), options);
   }
 
-  CFX_DIBExtractor temp(pSource);
-  RetainPtr<CFX_DIBitmap> pBitmap = temp.GetBitmap();
-  if (!pBitmap)
-    return false;
-  return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width,
+  return GDI_StretchDIBits(pSource, dest_left, dest_top, dest_width,
                            dest_height, options);
 }
 
@@ -157,7 +144,7 @@
   if (fabs(matrix.a) >= 0.5f || fabs(matrix.d) >= 0.5f)
     return false;
 
-  RetainPtr<CFX_DIBitmap> pTransformed =
+  RetainPtr<CFX_DIBBase> pTransformed =
       pSource->SwapXY(matrix.c > 0, matrix.b < 0);
   if (!pTransformed)
     return false;
diff --git a/core/fxge/win32/cpsoutput.cpp b/core/fxge/win32/cpsoutput.cpp
index 763a4e5..0b391ee 100644
--- a/core/fxge/win32/cpsoutput.cpp
+++ b/core/fxge/win32/cpsoutput.cpp
@@ -9,7 +9,7 @@
 #include <algorithm>
 
 #include "core/fxcrt/fx_system.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 CPSOutput::CPSOutput(HDC hDC, OutputMode mode) : m_hDC(hDC), m_mode(mode) {}
 
diff --git a/core/fxge/win32/cwin32_platform.cpp b/core/fxge/win32/cwin32_platform.cpp
index 5794e2e..428f132 100644
--- a/core/fxge/win32/cwin32_platform.cpp
+++ b/core/fxge/win32/cwin32_platform.cpp
@@ -14,8 +14,8 @@
 #include "core/fxge/cfx_folderfontinfo.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "third_party/base/check.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 #include "third_party/base/win/scoped_select_object.h"
 #include "third_party/base/win/win_util.h"
 
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index b97d881..c8aa54f 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -97,10 +97,6 @@
     ]
     allow_circular_includes_from += [ "fpdfxfa" ]
   }
-
-  if (pdf_use_skia) {
-    deps += [ "//skia" ]
-  }
 }
 
 pdfium_unittest_source_set("unittests") {
@@ -159,6 +155,7 @@
     "../core/fpdfapi/font",
     "../core/fpdfapi/page",
     "../core/fpdfapi/parser",
+    "../core/fxcrt:test_support",
     "../core/fxge",
   ]
   pdfium_root_dir = "../"
diff --git a/fpdfsdk/DEPS b/fpdfsdk/DEPS
index 14d2700..1e52515 100644
--- a/fpdfsdk/DEPS
+++ b/fpdfsdk/DEPS
@@ -2,10 +2,15 @@
   '+core',
   '+fxjs',
   '+public',
-  '+third_party/skia/include',
   '+v8',
   '+xfa/fgas/font',
   '+xfa/fgas/graphics',
   '+xfa/fwl',
   '+xfa/fxfa',
 ]
+
+specific_include_rules = {
+  'fpdf_view_embeddertest\.cpp': [
+    '+third_party/skia/include',
+  ],
+}
diff --git a/fpdfsdk/cpdfsdk_appstream.cpp b/fpdfsdk/cpdfsdk_appstream.cpp
index 030ea14..55c93bf 100644
--- a/fpdfsdk/cpdfsdk_appstream.cpp
+++ b/fpdfsdk/cpdfsdk_appstream.cpp
@@ -38,8 +38,8 @@
 #include "fpdfsdk/pwl/cpwl_edit.h"
 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
 #include "fpdfsdk/pwl/cpwl_wnd.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 namespace {
 
@@ -465,7 +465,6 @@
     float div = fHalfWidth * 0.75f;
     CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div);
     switch (nStyle) {
-      default:
       case BorderStyle::kSolid:
       case BorderStyle::kUnderline: {
         sColor = GetStrokeColorAppStream(color);
@@ -545,7 +544,6 @@
                                 const CFX_Color& crText) {
   CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
   switch (nStyle) {
-    default:
     case CheckStyle::kCheck:
       return GetAppStream_Check(rcCenter, crText);
     case CheckStyle::kCircle:
@@ -570,7 +568,6 @@
                                    const CFX_Color& crText) {
   CFX_FloatRect rcCenter = rcBBox.GetCenterSquare();
   switch (nStyle) {
-    default:
     case CheckStyle::kCheck:
       return GetAppStream_Check(rcCenter, crText);
     case CheckStyle::kCircle:
@@ -957,7 +954,6 @@
     AutoClosedQCommand q(&sAppStream);
 
     switch (nStyle) {
-      default:
       case BorderStyle::kSolid:
         sColor = GetFillColorAppStream(color);
         if (sColor.GetLength() > 0) {
diff --git a/fpdfsdk/cpdfsdk_baannot.cpp b/fpdfsdk/cpdfsdk_baannot.cpp
index a1751f9..c5d33d6 100644
--- a/fpdfsdk/cpdfsdk_baannot.cpp
+++ b/fpdfsdk/cpdfsdk_baannot.cpp
@@ -161,8 +161,6 @@
     case BorderStyle::kUnderline:
       name = "U";
       break;
-    default:
-      return;
   }
   pBSDict->SetNewFor<CPDF_Name>("S", name);
 }
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
index 84df6e1..9aade3a 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp
@@ -1040,7 +1040,6 @@
       break;
     case CPDF_Action::Type::kJavaScript:
       NOTREACHED_NORETURN();
-      break;
     case CPDF_Action::Type::kSetOCGState:
     case CPDF_Action::Type::kThread:
     case CPDF_Action::Type::kSound:
@@ -1123,7 +1122,6 @@
         break;
       default:
         NOTREACHED_NORETURN();
-        break;
     }
   });
 }
@@ -1170,7 +1168,6 @@
         break;
       default:
         NOTREACHED_NORETURN();
-        break;
     }
   });
 }
diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.h b/fpdfsdk/cpdfsdk_formfillenvironment.h
index f82f346..3be1962 100644
--- a/fpdfsdk/cpdfsdk_formfillenvironment.h
+++ b/fpdfsdk/cpdfsdk_formfillenvironment.h
@@ -28,7 +28,7 @@
 #include "fpdfsdk/pwl/cpwl_wnd.h"
 #include "fpdfsdk/pwl/ipwl_fillernotify.h"
 #include "public/fpdf_formfill.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CPDF_Action;
 class CPDF_FormField;
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index 076cac7..f66adbf 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -14,7 +14,6 @@
 #include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/cfx_path.h"
-#include "core/fxge/dib/cfx_dibitmap.h"
 #include "public/fpdf_doc.h"
 #include "public/fpdf_ext.h"
 #include "public/fpdfview.h"
@@ -39,6 +38,7 @@
 class CPDF_TextPageFind;
 class CPDFSDK_FormFillEnvironment;
 class CPDFSDK_InteractiveForm;
+class CFX_DIBitmap;
 struct CPDF_JavaScript;
 struct XObjectContext;
 
diff --git a/fpdfsdk/cpdfsdk_renderpage.cpp b/fpdfsdk/cpdfsdk_renderpage.cpp
index 9ffd683..757aa1f 100644
--- a/fpdfsdk/cpdfsdk_renderpage.cpp
+++ b/fpdfsdk/cpdfsdk_renderpage.cpp
@@ -69,6 +69,7 @@
     CPDF_AnnotList* pList = pOwnedList.get();
     pContext->m_pAnnots = std::move(pOwnedList);
     bool bPrinting =
+        (flags & FPDF_PRINTING) ||
         pContext->m_pDevice->GetDeviceType() != DeviceType::kDisplay;
 
     // TODO(https://crbug.com/pdfium/993) - maybe pass true here.
diff --git a/fpdfsdk/cpdfsdk_widget.cpp b/fpdfsdk/cpdfsdk_widget.cpp
index c336f5e..0b8083e 100644
--- a/fpdfsdk/cpdfsdk_widget.cpp
+++ b/fpdfsdk/cpdfsdk_widget.cpp
@@ -177,7 +177,6 @@
     case CPDF_AAction::kDocumentOpen:
     case CPDF_AAction::kNumberOfActions:
       NOTREACHED_NORETURN();
-      break;
   }
 
   return eEventType;
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
index 5137429..843af8c 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
@@ -6,6 +6,8 @@
 
 #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h"
 
+#include <algorithm>
+
 #include "constants/access_permissions.h"
 #include "constants/ascii.h"
 #include "constants/form_flags.h"
@@ -24,7 +26,6 @@
 #include "fpdfsdk/formfiller/cffl_textfield.h"
 #include "public/fpdf_fwlevent.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
 
 CFFL_InteractiveFormFiller::CFFL_InteractiveFormFiller(
     CallbackIface* pCallbackIface)
@@ -652,7 +653,7 @@
 
   constexpr float kMaxListBoxHeight = 140;
   const float fMaxListBoxHeight =
-      pdfium::clamp(kMaxListBoxHeight, fPopupMin, fPopupMax);
+      std::clamp(kMaxListBoxHeight, fPopupMin, fPopupMax);
 
   if (fBottom > fMaxListBoxHeight) {
     *fPopupRet = fMaxListBoxHeight;
diff --git a/fpdfsdk/formfiller/cffl_perwindowdata.cpp b/fpdfsdk/formfiller/cffl_perwindowdata.cpp
index 70dc696..85f6d03 100644
--- a/fpdfsdk/formfiller/cffl_perwindowdata.cpp
+++ b/fpdfsdk/formfiller/cffl_perwindowdata.cpp
@@ -7,7 +7,7 @@
 #include "fpdfsdk/formfiller/cffl_perwindowdata.h"
 
 #include "fpdfsdk/cpdfsdk_widget.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 
 CFFL_PerWindowData::CFFL_PerWindowData(CPDFSDK_Widget* pWidget,
                                        const CPDFSDK_PageView* pPageView,
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index 7c7d420..cb7c96d 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -39,8 +39,8 @@
 #include "fpdfsdk/cpdfsdk_interactiveform.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 06da6a8..9769570 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -29,7 +29,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/hash.h"
 #include "third_party/base/containers/contains.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 using pdfium::AnnotationStampWithApChecksum;
 
@@ -547,8 +547,13 @@
   }
   {
     const char* expected_hash = []() {
-      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "fad91b9c968fe8019a774f5e2419b8fc";
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+        return "6e00cc75639c5314c8273072915d8f92";
+#else
+        return "1fb0dd8dd5f0b9bb8d076e48eb59296d";
+#endif
+      }
       return "354002e1c4386d38fdde29ef8d61074a";
     }();
     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
@@ -768,8 +773,13 @@
 
   // Open the saved document.
   const char* checksum = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "899387ae792390cd0d83cf7e2bbebfb5";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+      return "24994ad69aa612a66d183eaf9a92aa06";
+#else
+      return "798fa41303381c9ba6d99092f5cd4d2b";
+#endif
+    }
     return "dba153419f67b7c0c0e3d22d3e8910d5";
   }();
 
@@ -887,7 +897,7 @@
 TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) {
   const char* md5_original = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "0dd4c099b93d24eed9926a948ac5101c";
+      return "2a9d1df839d5ec81a49f982347d9656c";
 #if BUILDFLAG(IS_APPLE)
     return "fc59468d154f397fd298c69f47ef565a";
 #else
@@ -896,7 +906,7 @@
   }();
   const char* md5_modified_highlight = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "92dfe7960d248635a694f43c66db7a4d";
+      return "0fb1653db0e8e8f7ce5d726bb0074bb5";
 #if BUILDFLAG(IS_APPLE)
     return "e64bf648f6e9354d1f3eedb47a2c9498";
 #else
@@ -905,7 +915,7 @@
   }();
   const char* md5_modified_square = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "fac81632e33fd5c06f39082a26d06ba8";
+      return "879c77a2cb9f79ba65ffe0bbdd720ce3";
 #if BUILDFLAG(IS_APPLE)
     return "a66591662c8e7ad3c6059952e234bebf";
 #else
@@ -1106,7 +1116,7 @@
 TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) {
   const char* md5_modified_path = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "f671765166acf45d80e833ea3aff8b90";
+      return "fb4d5fac05f7eb5d84a4100898c11197";
 #if BUILDFLAG(IS_APPLE)
     return "34614087e04b729b7b8c37739dcf9af9";
 #else
@@ -1115,7 +1125,7 @@
   }();
   const char* md5_two_paths = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "491ce8fb274cc83b55b6099f15457b5d";
+      return "fcf3e79b2a91d1294b9bbccff727d3c2";
 #if BUILDFLAG(IS_APPLE)
     return "6cdaf6b3e5145f435d8ccae6db5cf9af";
 #else
@@ -1124,7 +1134,7 @@
   }();
   const char* md5_new_annot = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "92bfb06058ff608571a3baf65f7fc05d";
+      return "7db6321c8ffe502f4e60622aa16d5417";
 #if BUILDFLAG(IS_APPLE)
     return "55dab4f158fdc284e439b88c4306373c";
 #else
@@ -1327,7 +1337,7 @@
 TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) {
   const char* md5_new_image = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "4ba31e174d873b3fda1d7a160d4a0e85";
+      return "476596330c0e7daa31f115005c1d36eb";
 #if BUILDFLAG(IS_APPLE)
     return "17ac49518eabbb6a7632a547269c40a3";
 #else
@@ -1336,7 +1346,7 @@
   }();
   const char* md5_modified_image = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "5806fadc1a192bc4bb07511a0711c957";
+      return "0047c3e7ea7658e1a963fc339f1c587d";
 #if BUILDFLAG(IS_APPLE)
     return "ce68959f74242d588af7fb82be5ba0ab";
 #else
@@ -1417,8 +1427,9 @@
 
 TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) {
   const char* md5_new_text = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "63b931799a9ba21c36d9d4f9711f252b";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "1e7f98c18775d6e0f4f454747b77cc1a";
+    }
 #if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
     return "0c3448974a4e8da2395da917935e5de1";
 #elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64)
@@ -1428,8 +1439,9 @@
 #endif
   }();
   const char* md5_modified_text = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "e29ddba6a49d5c9c5cdde7d1693a251c";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "37e35705946806f8f98c51e4e25647a2";
+    }
 #if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
     return "9cf1c024a9d2d356bcdd14cb71a32324";
 #elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64)
@@ -1567,7 +1579,7 @@
 
   const char* md5 = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "2b9078043cd6130fef4e8542dcda943e";
+      return "a95a65d109eda5671c793ff5f7d2a2df";
 #if BUILDFLAG(IS_APPLE)
     return "52e93c54796f7f7167edf64e81d12bd7";
 #else
@@ -3031,7 +3043,7 @@
   {
     const char* md5_sum = []() {
       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "08b693ba461a24ee6b9c7f86b5dbe191";
+        return "c09b129c071ec1569deb003676b617b0";
 #if BUILDFLAG(IS_APPLE)
       return "108a46c517c4eaace9982ee83e8e3296";
 #else
@@ -3059,7 +3071,7 @@
   {
     const char* md5_sum = []() {
       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "3e6ae77a45e49bfd909f3ee0351a4176";
+        return "277f1b9e70031539d034d22bc6064838";
 #if BUILDFLAG(IS_APPLE)
       return "eb3869335e7a219e1b5f25c1c6037b97";
 #else
@@ -3078,7 +3090,7 @@
   {
     const char* md5_sum = []() {
       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "12bb575d925923ad6672f427c574eef4";
+        return "d980005939cd4ae0a199d8600a0abdf3";
 #if BUILDFLAG(IS_APPLE)
       return "d20b1978da2362d3942ea0fc6d230997";
 #else
@@ -3653,18 +3665,24 @@
   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
 
   const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "238dccc7df0ac61ac580c28e1109da3c";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "522a4a6b6c7eab5bf95ded1f21ea372e";
-#endif
+    return "522a4a6b6c7eab5bf95ded1f21ea372e";
+#else
     return "12127303aecd80c6288460f7c0d79f3f";
+#endif
   }();
   const char* modified_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "0f326acb3eb583125ca584d703ccb13b";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "6844019e07b83cc01723415f58218d06";
-#endif
+    return "6844019e07b83cc01723415f58218d06";
+#else
     return "73d06ff4c665fe85029acef30240dcca";
+#endif
   }();
 
   {
diff --git a/fpdfsdk/fpdf_dataavail.cpp b/fpdfsdk/fpdf_dataavail.cpp
index e4f85eb..f0ce101 100644
--- a/fpdfsdk/fpdf_dataavail.cpp
+++ b/fpdfsdk/fpdf_dataavail.cpp
@@ -17,6 +17,7 @@
 #include "core/fxcrt/fx_stream.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_formfill.h"
 #include "third_party/base/numerics/safe_conversions.h"
@@ -64,7 +65,8 @@
   }
 
  private:
-  FX_FILEAVAIL* const avail_;
+  // TODO(tsepez): fix murky ownership in tests.
+  UNOWNED_PTR_EXCLUSION FX_FILEAVAIL* const avail_;
 };
 
 class FPDF_FileAccessContext final : public IFX_SeekableReadStream {
@@ -95,7 +97,8 @@
   explicit FPDF_FileAccessContext(FPDF_FILEACCESS* file) : file_(file) {}
   ~FPDF_FileAccessContext() override = default;
 
-  FPDF_FILEACCESS* const file_;
+  // TODO(tsepez): fix murky ownership in tests.
+  UNOWNED_PTR_EXCLUSION FPDF_FILEACCESS* const file_;
 };
 
 class FPDF_DownloadHintsContext final : public CPDF_DataAvail::DownloadHints {
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index 670892a..19097d8 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -48,26 +48,32 @@
 const wchar_t kBottomText[] = L"I'm at the bottom of the page";
 
 const char* BottomTextChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "c62d315856a558d2666b80d474831efe";
+  }
 #if BUILDFLAG(IS_APPLE)
-  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "81636489006a31fcb00cf29efcdf7909";
-#endif
+  return "81636489006a31fcb00cf29efcdf7909";
+#else
   return "891dcb6e914c8360998055f1f47c9727";
+#endif
 }
 
 const char* FirstRemovedChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "3006ab2b12d27246eae4faad509ac575";
+  }
 #if BUILDFLAG(IS_APPLE)
-  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "a1dc2812692fcc7ee4f01ca77435df9d";
-#endif
+  return "a1dc2812692fcc7ee4f01ca77435df9d";
+#else
   return "e1477dc3b5b3b9c560814c4d1135a02b";
+#endif
 }
 
 const wchar_t kLoadedFontText[] = L"I am testing my loaded font, WEE.";
 
 const char* LoadedFontTextChecksum() {
   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "d58570cc045dfb818b92cbabbd1a364c";
+    return "fc2334c350cbd0d2ae6076689da09741";
 #if BUILDFLAG(IS_APPLE)
   return "0f3e4a7d71f9e7eb8a1a0d69403b9848";
 #else
@@ -260,12 +266,21 @@
   FPDFPage_InsertObject(page.get(), text_object);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
 
-  const char kChecksum[] = "9a31fb87d1c6d2346bba22d1196041cd";
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+      return "9a31fb87d1c6d2346bba22d1196041cd";
+#else
+      return "5bb65e15fc0a685934cd5006dec08a76";
+#endif
+    }
+    return "9a31fb87d1c6d2346bba22d1196041cd";
+  }();
   ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
-  CompareBitmap(page_bitmap.get(), 400, 400, kChecksum);
+  CompareBitmap(page_bitmap.get(), 400, 400, checksum);
 
   ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  VerifySavedDocument(400, 400, kChecksum);
+  VerifySavedDocument(400, 400, checksum);
 }
 
 TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFontWithCharcodes) {
@@ -297,12 +312,21 @@
   FPDFPage_InsertObject(page.get(), text_object);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
 
-  const char kChecksum[] = "9a31fb87d1c6d2346bba22d1196041cd";
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+#if BUILDFLAG(IS_APPLE)
+      return "9a31fb87d1c6d2346bba22d1196041cd";
+#else
+      return "5bb65e15fc0a685934cd5006dec08a76";
+#endif
+    }
+    return "9a31fb87d1c6d2346bba22d1196041cd";
+  }();
   ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
-  CompareBitmap(page_bitmap.get(), 400, 400, kChecksum);
+  CompareBitmap(page_bitmap.get(), 400, 400, checksum);
 
   ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
-  VerifySavedDocument(400, 400, kChecksum);
+  VerifySavedDocument(400, 400, checksum);
 }
 
 TEST_F(FPDFEditEmbedderTest, EmptyCreation) {
@@ -718,11 +742,14 @@
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
 
   const char* changed_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "4a8345a139507932729e07d4831cbe2b";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "b720e83476fd6819d47c533f1f43c728";
-#endif
+    return "b720e83476fd6819d47c533f1f43c728";
+#else
     return "9a85b9354a69c61772ed24151c140f46";
+#endif
   }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
@@ -775,11 +802,14 @@
   ASSERT_TRUE(page);
 
   const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "3c04e3acc732faaf39fb0c19efd056ac";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "ae7a25c85e0e2dd0c5cb9dd5cd37f6df";
-#endif
+    return "ae7a25c85e0e2dd0c5cb9dd5cd37f6df";
+#else
     return "7af7fe5b281298261eb66ac2d22f5054";
+#endif
   }();
   {
     // When opened before any editing and saving, the clipping path is rendered.
@@ -840,11 +870,14 @@
   ASSERT_TRUE(page);
 
   const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "d76a31d931a350f0809226a41029a9a4";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "1226bc2b8072622eb28f52321876e814";
-#endif
+    return "1226bc2b8072622eb28f52321876e814";
+#else
     return "c5241eef60b9eac68ed1f2a5fd002703";
+#endif
   }();
   {
     // When opened before any editing and saving, the text object is rendered.
@@ -1243,7 +1276,7 @@
   {
     const char* original_checksum = []() {
       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "34ac4e0f3ba510760be09d0e19c1b43e";
+        return "efc2206b313fff03be8e701907322b06";
 #if BUILDFLAG(IS_APPLE)
 #ifdef ARCH_CPU_ARM64
       return "cdc8e22cf1e7e06999dc456288672a3b";
@@ -1293,7 +1326,7 @@
   EXPECT_EQ(11, FPDFPage_CountObjects(page));
   const char* non_primes_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "c671fa07b63e85c4201b9926e880fda8";
+      return "10a6558c9e40ea837922e6f2882a2d57";
 #if BUILDFLAG(IS_APPLE)
 #ifdef ARCH_CPU_ARM64
     return "23c4aec321547f51591fe7363a9ea2d6";
@@ -1306,7 +1339,7 @@
   }();
   const char* non_primes_after_save_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "c671fa07b63e85c4201b9926e880fda8";
+      return "10a6558c9e40ea837922e6f2882a2d57";
 #if BUILDFLAG(IS_APPLE)
 #ifdef ARCH_CPU_ARM64
     return "6bb1ea0d0a512f29edabda33064a0725";
@@ -1591,11 +1624,14 @@
   // Verify the "Hello, world!" text is gone.
   ASSERT_EQ(2, FPDFPage_CountObjects(page));
   const char* hello_removed_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "204c11472f5b93719487de7b9c1b1c93";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "5508c2f06d104050f74f655693e38c2c";
-#endif
+    return "5508c2f06d104050f74f655693e38c2c";
+#else
     return "a8cd82499cf744e0862ca468c9d4ceb8";
+#endif
   }();
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
@@ -1750,7 +1786,7 @@
 
   const char* stream1_removed_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "b9bb0acfdba4bb5d2e578e7b73341baf";
+      return "0b3ef335b8d86a3f9d609368b9d075e0";
 #if BUILDFLAG(IS_APPLE)
 #if ARCH_CPU_ARM64
     return "08505db7b598f7397a2260ecb1f6d86d";
@@ -2268,7 +2304,7 @@
   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
   const char* checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "8a48b019826492331454f2809990aba8";
+      return "72523cfac069f8a81057164682998961";
 #if BUILDFLAG(IS_APPLE)
     return "279693baca9f48da2d75a8e289aed58e";
 #else
@@ -2453,11 +2489,14 @@
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
     const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "3fa05f8935a43a38a8923e9d5fb94365";
+      }
 #if BUILDFLAG(IS_APPLE)
-      if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "983baaa1f688eff7a14b1bf91c171a1a";
-#endif
+      return "983baaa1f688eff7a14b1bf91c171a1a";
+#else
       return "161523e196eb5341604cd73e12c97922";
+#endif
     }();
     CompareBitmap(page_bitmap.get(), 612, 792, checksum);
 
@@ -2477,11 +2516,14 @@
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
     const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "63385a217934d9ee9e17ef4d7f7b2128";
+      }
 #if BUILDFLAG(IS_APPLE)
-      if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "e0b3493c5c16e41d0d892ffb48e63fba";
-#endif
+      return "e0b3493c5c16e41d0d892ffb48e63fba";
+#else
       return "1fbf772dca8d82b960631e6683934964";
+#endif
     }();
     CompareBitmap(page_bitmap.get(), 612, 792, checksum);
 
@@ -2558,7 +2600,7 @@
 TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) {
   const char* original_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "39a4ac8f1fdc6653edd3b91862ea7b75";
+      return "48c7f21b2a1a1bbeab24cccccc131e47";
 #if BUILDFLAG(IS_APPLE)
     return "c488514ce0fc949069ff560407edacd2";
 #else
@@ -2885,18 +2927,24 @@
 
 TEST_F(FPDFEditEmbedderTest, ModifyFormObject) {
   const char* orig_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "1c6dae4b04fea7430a791135721eaba5";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "a637057185f50aac1aa5490f726aef95";
-#endif
+    return "a637057185f50aac1aa5490f726aef95";
+#else
     return "34a9ec0a9581a7970e073c0bcc4ca676";
+#endif
   }();
   const char* new_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "7282fe98693c0a7ad2c1b3f3f9563977";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "8ad9d79b02b609ff734e2a2195c96e2d";
-#endif
+    return "8ad9d79b02b609ff734e2a2195c96e2d";
+#else
     return "609b5632a21c886fa93182dbc290bf7a";
+#endif
   }();
 
   ASSERT_TRUE(OpenDocument("form_object.pdf"));
@@ -3320,7 +3368,7 @@
   ScopedFPDFBitmap page_bitmap2 = RenderPage(page);
   const char* insert_true_type_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "683f4a385a891494100192cb338b11f0";
+      return "4f9a6c7752ac7d4e4c731260fdb5af15";
 #if BUILDFLAG(IS_APPLE)
     return "c7e2271a7f30e5b919a13ead47cea105";
 #else
@@ -3397,10 +3445,16 @@
   }
 
   // Check that the text renders properly.
-  static constexpr char kChecksum[] = "84d31d11b76845423a2cfc1879c0fbb9";
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "2e174d17de96a760d42ca3a06acbf36a";
+    }
+    return "84d31d11b76845423a2cfc1879c0fbb9";
+  }();
+
   {
     ScopedFPDFBitmap page_bitmap = RenderPage(page);
-    CompareBitmap(page_bitmap.get(), 612, 792, kChecksum);
+    CompareBitmap(page_bitmap.get(), 612, 792, checksum);
   }
 
   // Save the document, close the page.
@@ -3408,7 +3462,7 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   FPDF_ClosePage(page);
 
-  VerifySavedDocument(612, 792, kChecksum);
+  VerifySavedDocument(612, 792, checksum);
 }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
@@ -4141,13 +4195,15 @@
   ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page));
 
   const char* smask_checksum = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
       return "0653a18f3bf9b4d8413a2aa10bc11c38";
+    }
     return "5a3ae4a660ce919e29c42ec2258142f1";
   }();
   const char* no_smask_checksum = []() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "d568afc908d595224d804448d5d3672f";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "0da49e63e7d6337aca78b19938e3bf65";
+    }
     return "67504e83f5d78214ea00efc19082c5c1";
   }();
 
@@ -4477,21 +4533,31 @@
     ScopedFPDFBitmap bitmap(
         FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1));
     ASSERT_TRUE(bitmap);
-    const char kChecksum[] = "bb0abe1accca1cfeaaf78afa35762350";
-    CompareBitmap(bitmap.get(), 64, 11, kChecksum);
+    const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "b17801afe8a36d6aad6c2239b88f2a73";
+      }
+      return "bb0abe1accca1cfeaaf78afa35762350";
+    }();
+    CompareBitmap(bitmap.get(), 64, 11, checksum);
 
     ScopedFPDFBitmap x2_bitmap(
         FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 2.4f));
     ASSERT_TRUE(x2_bitmap);
-    const char kX2Checksum[] = "80db528ec7146d92247f2339a8f10ba5";
-    CompareBitmap(x2_bitmap.get(), 153, 25, kX2Checksum);
+    const char* x2_checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "33af8b151ab26ebce5a71b39eedea6b1";
+      }
+      return "80db528ec7146d92247f2339a8f10ba5";
+    }();
+    CompareBitmap(x2_bitmap.get(), 153, 25, x2_checksum);
 
     ScopedFPDFBitmap x10_bitmap(
         FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10));
     ASSERT_TRUE(x10_bitmap);
     const char* x10_checksum = []() {
       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "f5e86739b9603838cbfb0c7f8e54e4ae";
+        return "93dd7ad07bdaaba9ecd268350cb91596";
       return "149f63de758ab01d3b75605cdfd4c176";
     }();
     CompareBitmap(x10_bitmap.get(), 631, 103, x10_checksum);
@@ -4504,21 +4570,31 @@
     ScopedFPDFBitmap bitmap(
         FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1));
     ASSERT_TRUE(bitmap);
-    const char kChecksum[] = "3fc1101b2408c5484adc24ba0a11ff3d";
-    CompareBitmap(bitmap.get(), 116, 16, kChecksum);
+    const char* checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "63fd059d984a5bea10f27ba026420202";
+      }
+      return "3fc1101b2408c5484adc24ba0a11ff3d";
+    }();
+    CompareBitmap(bitmap.get(), 116, 16, checksum);
 
     ScopedFPDFBitmap x2_bitmap(
         FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 2.4f));
     ASSERT_TRUE(x2_bitmap);
-    const char kX2Checksum[] = "429960ae7b822f0c630432535e637465";
-    CompareBitmap(x2_bitmap.get(), 276, 36, kX2Checksum);
+    const char* x2_checksum = []() {
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+        return "fc45021e3ea3ebd406fe6ffaa8c5c5b7";
+      }
+      return "429960ae7b822f0c630432535e637465";
+    }();
+    CompareBitmap(x2_bitmap.get(), 276, 36, x2_checksum);
 
     ScopedFPDFBitmap x10_bitmap(
         FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10));
     ASSERT_TRUE(x10_bitmap);
     const char* x10_checksum = []() {
       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-        return "59d0b5f1fc2a1cc0c392e213909bbbb6";
+        return "61476636eaa0da0b93d8b1937cf22b75";
       return "f5f93bf64de579b59e775d7076ca0a5a";
     }();
     CompareBitmap(x10_bitmap.get(), 1143, 150, x10_checksum);
@@ -4538,21 +4614,31 @@
   ScopedFPDFBitmap bitmap(
       FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1));
   ASSERT_TRUE(bitmap);
-  const char kChecksum[] = "08ada0802f780d3fefb161dc6fb45977";
-  CompareBitmap(bitmap.get(), 29, 28, kChecksum);
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "f515a7209d7892065d3716ec462f5c10";
+    }
+    return "08ada0802f780d3fefb161dc6fb45977";
+  }();
+  CompareBitmap(bitmap.get(), 29, 28, checksum);
 
   ScopedFPDFBitmap x2_bitmap(
       FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 2.4f));
   ASSERT_TRUE(x2_bitmap);
-  const char kX2Checksum[] = "09d7ddb647b8653cb59aede349a0c3e1";
-  CompareBitmap(x2_bitmap.get(), 67, 67, kX2Checksum);
+  const char* x2_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "c69bbe5318ec149f63228e276e708612";
+    }
+    return "09d7ddb647b8653cb59aede349a0c3e1";
+  }();
+  CompareBitmap(x2_bitmap.get(), 67, 67, x2_checksum);
 
   ScopedFPDFBitmap x10_bitmap(
       FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10));
   ASSERT_TRUE(x10_bitmap);
   const char* x10_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "839dc0eed66eab6d545833f25b37031e";
+      return "bb7c2ec575f27cf882dcd38f2563c00f";
     return "bbd3842a4b50dbfcbce4eee2b067a297";
   }();
   CompareBitmap(x10_bitmap.get(), 275, 275, x10_checksum);
@@ -4595,8 +4681,13 @@
   ScopedFPDFBitmap bitmap(
       FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object.get(), 1));
   ASSERT_TRUE(bitmap);
-  const char kChecksum[] = "fa947759dab76d68a07ccf6f97b2d9c2";
-  CompareBitmap(bitmap.get(), 151, 12, kChecksum);
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "574ae982d02e653ab6a8f23a6cdf4085";
+    }
+    return "fa947759dab76d68a07ccf6f97b2d9c2";
+  }();
+  CompareBitmap(bitmap.get(), 151, 12, checksum);
 }
 
 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForTextWithBadParameters) {
@@ -4647,3 +4738,33 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFEditEmbedderTest, MultipleGraphicsStates) {
+  ASSERT_TRUE(OpenDocument("multiple_graphics_states.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFPageObject path(FPDFPageObj_CreateNewPath(400, 100));
+    EXPECT_TRUE(FPDFPageObj_SetFillColor(path.get(), 255, 0, 0, 255));
+    EXPECT_TRUE(FPDFPath_SetDrawMode(path.get(), FPDF_FILLMODE_ALTERNATE, 0));
+    EXPECT_TRUE(FPDFPath_MoveTo(path.get(), 100, 100));
+    EXPECT_TRUE(FPDFPath_LineTo(path.get(), 100, 125));
+    EXPECT_TRUE(FPDFPath_Close(path.get()));
+
+    FPDFPage_InsertObject(page, path.release());
+    EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  }
+
+  const char* checksum = CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()
+                             ? "7ebec75d95c64b522999a710de76c52c"
+                             : "f4b36616a7fea81a4f06cc7b01a55ac1";
+
+  ScopedFPDFBitmap bitmap = RenderPage(page);
+  CompareBitmap(bitmap.get(), 200, 300, checksum);
+
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedDocument(200, 300, checksum);
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_editimg.cpp b/fpdfsdk/fpdf_editimg.cpp
index 3fae5b4..8e3402f 100644
--- a/fpdfsdk/fpdf_editimg.cpp
+++ b/fpdfsdk/fpdf_editimg.cpp
@@ -24,6 +24,7 @@
 #include "core/fpdfapi/render/cpdf_renderstatus.h"
 #include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "fpdfsdk/cpdfsdk_customaccess.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 6cc64f2..3e6d7fd 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -34,10 +34,10 @@
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_annotlist.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_formfill.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
 #ifdef PDF_ENABLE_XFA
@@ -202,7 +202,7 @@
   if (!pDoc)
     return nullptr;
 
-  page_index = pdfium::clamp(page_index, 0, pDoc->GetPageCount());
+  page_index = std::clamp(page_index, 0, pDoc->GetPageCount());
   RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(page_index));
   if (!pPageDict)
     return nullptr;
@@ -967,7 +967,8 @@
   if (dash_vector.size() > dash_count)
     return false;
 
-  memcpy(dash_array, dash_vector.data(), dash_vector.size() * sizeof(float));
+  FXSYS_memcpy(dash_array, dash_vector.data(),
+               dash_vector.size() * sizeof(float));
   return true;
 }
 
diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp
index c74d6dc..e6eba89 100644
--- a/fpdfsdk/fpdf_editpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp
@@ -457,3 +457,50 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFEditPageEmbedderTest, VerifyDashArraySaved) {
+  constexpr float kDashArray[] = {2.5, 3.6};
+  constexpr float kDashPhase = 1.2;
+
+  CreateEmptyDocument();
+  {
+    ScopedFPDFPage page(FPDFPage_New(document(), 0, 612, 792));
+
+    FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
+    EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(path, 2));
+    EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, 255, 0, 0, 255));
+    EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_NONE, 1));
+    EXPECT_TRUE(FPDFPath_LineTo(path, 200, 200));
+    EXPECT_TRUE(FPDFPageObj_SetDashArray(path, kDashArray,
+                                         std::size(kDashArray), kDashPhase));
+    FPDFPage_InsertObject(page.get(), path);
+
+    EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+    path = FPDFPage_GetObject(page.get(), 0);
+    ASSERT_TRUE(path);
+    ASSERT_EQ(2, FPDFPageObj_GetDashCount(path));
+
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  }
+
+  ASSERT_TRUE(OpenSavedDocument());
+  FPDF_PAGE page = LoadSavedPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(path);
+
+  float dash_array[] = {0, 0};
+  ASSERT_EQ(static_cast<int>(std::size(dash_array)),
+            FPDFPageObj_GetDashCount(path));
+  ASSERT_TRUE(
+      FPDFPageObj_GetDashArray(path, dash_array, std::size(dash_array)));
+  ASSERT_EQ(kDashArray[0], dash_array[0]);
+  ASSERT_EQ(kDashArray[1], dash_array[1]);
+  float dash_phase = 0;
+  ASSERT_TRUE(FPDFPageObj_GetDashPhase(path, &dash_phase));
+  ASSERT_EQ(kDashPhase, dash_phase);
+
+  CloseSavedPage(page);
+  CloseSavedDocument();
+}
diff --git a/fpdfsdk/fpdf_editpath.cpp b/fpdfsdk/fpdf_editpath.cpp
index 4d481f8..c0b8db3 100644
--- a/fpdfsdk/fpdf_editpath.cpp
+++ b/fpdfsdk/fpdf_editpath.cpp
@@ -12,7 +12,7 @@
 #include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/stl_util.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 // These checks are here because core/ and public/ cannot depend on each other.
 static_assert(static_cast<int>(CFX_GraphStateData::LineCap::kButt) ==
diff --git a/fpdfsdk/fpdf_edittext.cpp b/fpdfsdk/fpdf_edittext.cpp
index 467a8f3..55ac4f7 100644
--- a/fpdfsdk/fpdf_edittext.cpp
+++ b/fpdfsdk/fpdf_edittext.cpp
@@ -31,8 +31,10 @@
 #include "core/fxcrt/fx_string_wrappers.h"
 #include "core/fxcrt/span_util.h"
 #include "core/fxcrt/stl_util.h"
+#include "core/fxcrt/utf16.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/text_char_pos.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
@@ -166,8 +168,9 @@
 // PDF spec 1.7 Section 5.9.2: "Unicode character sequences as expressed in
 // UTF-16BE encoding." See https://en.wikipedia.org/wiki/UTF-16#Description
 void AddUnicode(fxcrt::ostringstream* pBuffer, uint32_t unicode) {
-  if (unicode >= 0xD800 && unicode <= 0xDFFF)
+  if (pdfium::IsHighSurrogate(unicode) || pdfium::IsLowSurrogate(unicode)) {
     unicode = 0;
+  }
 
   char ans[8];
   *pBuffer << "<";
@@ -369,16 +372,18 @@
   uint32_t dwGlyphIndex;
   uint32_t dwCurrentChar = static_cast<uint32_t>(
       FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex));
-  static constexpr uint32_t kMaxUnicode = 0x10FFFF;
   // If it doesn't have a single char, just fail
-  if (dwGlyphIndex == 0 || dwCurrentChar > kMaxUnicode)
+  if (dwGlyphIndex == 0 ||
+      dwCurrentChar > pdfium::kMaximumSupplementaryCodePoint) {
     return nullptr;
+  }
 
   std::multimap<uint32_t, uint32_t> to_unicode;
   std::map<uint32_t, uint32_t> widths;
   while (true) {
-    if (dwCurrentChar > kMaxUnicode)
+    if (dwCurrentChar > pdfium::kMaximumSupplementaryCodePoint) {
       break;
+    }
 
     if (!pdfium::Contains(widths, dwGlyphIndex))
       widths[dwGlyphIndex] = pFont->GetGlyphWidth(dwGlyphIndex);
diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp
index 4eb8ebe..c62a4ba 100644
--- a/fpdfsdk/fpdf_flatten.cpp
+++ b/fpdfsdk/fpdf_flatten.cpp
@@ -149,9 +149,6 @@
       for (size_t i = 0; i < nRects; i++)
         pArray[i] = array[i].bottom;
       break;
-    default:
-      NOTREACHED_NORETURN();
-      return 0.0f;
   }
 
   float fRet = pArray[0];
diff --git a/fpdfsdk/fpdf_flatten_embeddertest.cpp b/fpdfsdk/fpdf_flatten_embeddertest.cpp
index 235ab88..f3305b5 100644
--- a/fpdfsdk/fpdf_flatten_embeddertest.cpp
+++ b/fpdfsdk/fpdf_flatten_embeddertest.cpp
@@ -42,7 +42,7 @@
 TEST_F(FPDFFlattenEmbedderTest, BUG_861842) {
   const char* checkbox_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "252fd5f2299cc16e5a07565df7c30565";
+      return "95fdaa000e81c80892b8d370f77be970";
 #if BUILDFLAG(IS_APPLE)
     return "6aafcb2d98da222964bcdbf5aa1f4f1f";
 #else
@@ -70,7 +70,7 @@
 TEST_F(FPDFFlattenEmbedderTest, BUG_889099) {
   const char* page_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "73678f308625e22f31940e9f732b68bf";
+      return "de7119d99f42deab2f4215017bdb16af";
 #if BUILDFLAG(IS_APPLE)
     return "049ed3f1e21fc72f929af3410c64bc8f";
 #else
@@ -78,11 +78,14 @@
 #endif
   }();
   const char* flattened_page_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "7978c7b3d643a5f0ac0f03ce759c55fe";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "41debc60cf2a8f74c710ec6082d77b18";
-#endif
+    return "41debc60cf2a8f74c710ec6082d77b18";
+#else
     return "0832157462ea70fbbf053e14b1d6457f";
+#endif
   }();
 
   ASSERT_TRUE(OpenDocument("bug_889099.pdf"));
diff --git a/fpdfsdk/fpdf_formfill.cpp b/fpdfsdk/fpdf_formfill.cpp
index 8c1a083..ee72562 100644
--- a/fpdfsdk/fpdf_formfill.cpp
+++ b/fpdfsdk/fpdf_formfill.cpp
@@ -21,6 +21,7 @@
 #include "core/fpdfdoc/cpdf_formfield.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "fpdfsdk/cpdfsdk_annot.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
@@ -31,7 +32,13 @@
 #ifdef PDF_ENABLE_XFA
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
+#endif  // PDF_ENABLE_XFA
 
+#if defined(_SKIA_SUPPORT_)
+class SkCanvas;
+#endif  // defined(_SKIA_SUPPORT_)
+
+#ifdef PDF_ENABLE_XFA
 static_assert(static_cast<int>(AlertButton::kDefault) ==
                   JSPLATFORM_ALERT_BUTTON_DEFAULT,
               "Default alert button types must match");
@@ -171,7 +178,7 @@
 
 void FFLCommon(FPDF_FORMHANDLE hHandle,
                FPDF_BITMAP bitmap,
-               FPDF_RECORDER recorder,
+               FPDF_SKIA_CANVAS canvas,
                FPDF_PAGE fpdf_page,
                int start_x,
                int start_y,
@@ -194,8 +201,9 @@
 
   auto pDevice = std::make_unique<CFX_DefaultRenderDevice>();
 #if defined(_SKIA_SUPPORT_)
-  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    pDevice->AttachRecorder(static_cast<SkPictureRecorder*>(recorder));
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() && canvas) {
+    pDevice->AttachCanvas(reinterpret_cast<SkCanvas*>(canvas));
+  }
 #endif
 
   RetainPtr<CFX_DIBitmap> holder(CFXDIBitmapFromFPDFBitmap(bitmap));
@@ -682,16 +690,16 @@
 }
 
 #if defined(_SKIA_SUPPORT_)
-FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLRecord(FPDF_FORMHANDLE hHandle,
-                                              FPDF_RECORDER recorder,
-                                              FPDF_PAGE page,
-                                              int start_x,
-                                              int start_y,
-                                              int size_x,
-                                              int size_y,
-                                              int rotate,
-                                              int flags) {
-  FFLCommon(hHandle, nullptr, recorder, page, start_x, start_y, size_x, size_y,
+FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDrawSkia(FPDF_FORMHANDLE hHandle,
+                                                FPDF_SKIA_CANVAS canvas,
+                                                FPDF_PAGE page,
+                                                int start_x,
+                                                int start_y,
+                                                int size_x,
+                                                int size_y,
+                                                int rotate,
+                                                int flags) {
+  FFLCommon(hHandle, nullptr, canvas, page, start_x, start_y, size_x, size_y,
             rotate, flags);
 }
 #endif
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index ea4af57..3f3f51e 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -1321,7 +1321,7 @@
 TEST_F(FPDFFormFillEmbedderTest, FormText) {
   const char* focused_text_form_with_abc_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "07a179a9dfb8f5462746262984109a99";
+      return "b9fb2245a98ac48146da84237a37f8cc";
 #if BUILDFLAG(IS_APPLE)
     return "9fb14198d75ca0a107060c60ca21b0c7";
 #else
@@ -1330,7 +1330,7 @@
   }();
   const char* unfocused_text_form_with_abc_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "a21b74cc620db8a9891ebd69e1aeda98";
+      return "5f3205f0189d9dde54665f970838f614";
 #if BUILDFLAG(IS_APPLE)
     return "3c3209357e0c057a0620afa7d83eb784";
 #else
@@ -1432,7 +1432,7 @@
 TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditFirstForm) {
   const char* checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "29a06da3e47f67535e266b090a5ac82d";
+      return "143c2bb79fcaecf24f5aa104dce27beb";
 #if BUILDFLAG(IS_APPLE)
     return "bf5423874f188427d2500a2bc4abebbe";
 #else
@@ -1465,7 +1465,7 @@
 TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditSecondForm) {
   const char* checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "19f8574d6378ee36e349376d88b7a2c4";
+      return "e36726414acb616dc203e8851b510e2c";
 #if BUILDFLAG(IS_APPLE)
     return "8a0fd8772dba6e1e952e49d159cc64b5";
 #else
@@ -1498,7 +1498,7 @@
 TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditBothForms) {
   const char* checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "edbc9b0e190118a9039fffc11e494081";
+      return "f82a807c056e22aa55d3d7228eedfe6f";
 #if BUILDFLAG(IS_APPLE)
     return "1f422ee1c520ad74b1a993b64bd4dc4a";
 #else
@@ -1537,11 +1537,14 @@
 
 TEST_F(FPDFFormFillEmbedderTest, RemoveFormFieldHighlight) {
   const char* no_highlight_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "3bfddb2529085021ad283b7e65f71525";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "5c82aa43e3b478aa1e4c94bb9ef1f11f";
-#endif
+    return "5c82aa43e3b478aa1e4c94bb9ef1f11f";
+#else
     return "a6268304f7eedfa9ee98fac3caaf2efb";
+#endif
   }();
 
   ASSERT_TRUE(OpenDocument("text_form.pdf"));
@@ -3365,7 +3368,7 @@
   }
 
   void SetFocusOnNthAnnot(size_t n) {
-    DCHECK_NE(n, 0);
+    DCHECK_NE(n, 0u);
     // Setting focus on first annot.
     FORM_OnMouseMove(form_handle(), page(), /*modifier=*/0, 100, 680);
     FORM_OnLButtonDown(form_handle(), page(), /*modifier=*/0, 100, 680);
diff --git a/fpdfsdk/fpdf_ppo.cpp b/fpdfsdk/fpdf_ppo.cpp
index f1fde22..9efb523 100644
--- a/fpdfsdk/fpdf_ppo.cpp
+++ b/fpdfsdk/fpdf_ppo.cpp
@@ -38,7 +38,7 @@
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 struct XObjectContext {
   UnownedPtr<CPDF_Document> dest_doc;
diff --git a/fpdfsdk/fpdf_progressive.cpp b/fpdfsdk/fpdf_progressive.cpp
index af2cc47..83b5797 100644
--- a/fpdfsdk/fpdf_progressive.cpp
+++ b/fpdfsdk/fpdf_progressive.cpp
@@ -13,6 +13,7 @@
 #include "core/fpdfapi/render/cpdf_pagerendercontext.h"
 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/cpdfsdk_pauseadapter.h"
 #include "fpdfsdk/cpdfsdk_renderpage.h"
@@ -55,18 +56,17 @@
   if (!pPage)
     return FPDF_RENDER_FAILED;
 
-  auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
-  pPage->SetRenderContext(std::move(pOwnedContext));
+  auto owned_context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* context = owned_context.get();
+  pPage->SetRenderContext(std::move(owned_context));
 
   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
-  auto pOwnedDevice = std::make_unique<CFX_DefaultRenderDevice>();
-  CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
-  pContext->m_pDevice = std::move(pOwnedDevice);
-  pDevice->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER));
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  device->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER));
+  context->m_pDevice = std::move(device);
 
   CPDFSDK_PauseAdapter pause_adapter(pause);
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+  CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
                                 size_y, rotate, flags, color_scheme,
                                 /*need_to_restore=*/false, &pause_adapter);
 
@@ -76,10 +76,11 @@
   }
 #endif  // defined(_SKIA_SUPPORT_)
 
-  if (!pContext->m_pRenderer)
+  if (!context->m_pRenderer) {
     return FPDF_RENDER_FAILED;
+  }
 
-  return ToFPDFStatus(pContext->m_pRenderer->GetStatus());
+  return ToFPDFStatus(context->m_pRenderer->GetStatus());
 }
 
 FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap,
diff --git a/fpdfsdk/fpdf_structtree.cpp b/fpdfsdk/fpdf_structtree.cpp
index 76a30be..4817f47 100644
--- a/fpdfsdk/fpdf_structtree.cpp
+++ b/fpdfsdk/fpdf_structtree.cpp
@@ -135,6 +135,10 @@
   if (!elem)
     return -1;
   RetainPtr<const CPDF_Object> attr_obj = elem->GetA();
+  if (!attr_obj) {
+    return -1;
+  }
+  attr_obj = attr_obj->GetDirect();
   if (!attr_obj)
     return -1;
   if (attr_obj->IsArray())
@@ -154,6 +158,10 @@
   if (!attr_obj)
     return nullptr;
 
+  attr_obj = attr_obj->GetDirect();
+  if (!attr_obj) {
+    return nullptr;
+  }
   if (attr_obj->IsDictionary()) {
     return index == 0 ? FPDFStructElementAttrFromCPDFDictionary(
                             attr_obj->AsDictionary())
@@ -288,8 +296,9 @@
                                 void* buffer,
                                 unsigned long buflen,
                                 unsigned long* out_buflen) {
-  if (!out_buflen || !buffer)
+  if (!out_buflen) {
     return false;
+  }
 
   const CPDF_Dictionary* dict =
       CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
@@ -352,7 +361,7 @@
   if (!dict)
     return false;
 
-  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor(name);
+  RetainPtr<const CPDF_Object> obj = dict->GetDirectObjectFor(name);
   if (!obj || !obj->IsNumber())
     return false;
 
diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp
index 472a3a9..2704887 100644
--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp
+++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp
@@ -595,6 +595,11 @@
       ASSERT_EQ(2, FPDF_StructElement_Attr_GetCount(attr));
       ASSERT_FALSE(
           FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0U, nullptr));
+      unsigned long buffer_len_needed = ULONG_MAX;
+      // Pass buffer = nullptr to obtain the size of the buffer needed,
+      ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0,
+                                                  &buffer_len_needed));
+      EXPECT_EQ(2U, buffer_len_needed);
       char buffer[8] = {};
       unsigned long out_len = ULONG_MAX;
       // Deliberately pass in a small buffer size to make sure `buffer` remains
@@ -643,11 +648,12 @@
       FPDF_STRUCTELEMENT td = FPDF_StructElement_GetChildAtIndex(tr, 1);
       ASSERT_TRUE(td);
       {
+        // Test counting and obtaining attributes via reference
         ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(td));
         FPDF_STRUCTELEMENT_ATTR attr =
             FPDF_StructElement_GetAttributeAtIndex(td, 0);
         ASSERT_TRUE(attr);
-        ASSERT_EQ(3, FPDF_StructElement_Attr_GetCount(attr));
+        ASSERT_EQ(4, FPDF_StructElement_Attr_GetCount(attr));
         // Test string and blob type
         {
           char buffer[16] = {};
@@ -691,6 +697,23 @@
               FPDF_StructElement_Attr_GetBooleanValue(attr, buffer, &val));
           EXPECT_TRUE(val);
         }
+
+        // Test reference to number
+        {
+          char buffer[16] = {};
+          unsigned long out_len = ULONG_MAX;
+          ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
+              attr, 3, buffer, sizeof(buffer), &out_len));
+          EXPECT_EQ(8U, out_len);
+          EXPECT_STREQ("RowSpan", buffer);
+
+          EXPECT_EQ(FPDF_OBJECT_REFERENCE,
+                    FPDF_StructElement_Attr_GetType(attr, buffer));
+          float val;
+          ASSERT_TRUE(
+              FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &val));
+          EXPECT_FLOAT_EQ(3, val);
+        }
       }
     }
   }
@@ -831,3 +854,18 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFStructTreeEmbedderTest, Bug1443100) {
+  ASSERT_TRUE(OpenDocument("tagged_table_bad_parent.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Calling these APIs should not trigger a dangling pointer.
+    ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+    ASSERT_TRUE(struct_tree);
+    ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+  }
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index 9814da6..c9ea333 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -26,8 +26,8 @@
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_path.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 
 namespace {
 
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index d65579d..f93561c 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -40,6 +40,7 @@
 #include "core/fxge/cfx_defaultrenderdevice.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_renderdevice.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
 #include "fpdfsdk/cpdfsdk_customaccess.h"
 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
@@ -48,14 +49,9 @@
 #include "fxjs/ijs_runtime.h"
 #include "public/fpdf_formfill.h"
 #include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/span.h"
-
-#if defined(_SKIA_SUPPORT_)
-#include "third_party/skia/include/core/SkPictureRecorder.h"  // nogncheck
-#include "third_party/skia/include/core/SkRect.h"             // nogncheck
-#endif  // defined(_SKIA_SUPPORT_)
 
 #ifdef PDF_ENABLE_V8
 #include "fxjs/cfx_v8_array_buffer_allocator.h"
@@ -72,6 +68,10 @@
 #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
 #include "public/fpdf_edit.h"
 
+#if defined(_SKIA_SUPPORT_)
+class SkCanvas;
+#endif  // defined(_SKIA_SUPPORT_)
+
 // These checks are here because core/ and public/ cannot depend on each other.
 static_assert(static_cast<int>(WindowsPrintMode::kEmf) == FPDF_PRINTMODE_EMF,
               "WindowsPrintMode::kEmf value mismatch");
@@ -546,10 +546,10 @@
   if (!pPage)
     return;
 
-  auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  auto owned_context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* context = owned_context.get();
   CPDF_Page::RenderContextClearer clearer(pPage);
-  pPage->SetRenderContext(std::move(pOwnedContext));
+  pPage->SetRenderContext(std::move(owned_context));
 
   // Don't render the full page to bitmap for a mask unless there are a lot
   // of masks. Full page bitmaps result in large spool sizes, so they should
@@ -564,9 +564,9 @@
   const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
   auto* render_data = CPDF_DocRenderData::FromDocument(pPage->GetDocument());
   if (!bNewBitmap && !bHasMask) {
-    pContext->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
+    context->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
         dc, render_data->GetPSFontTracker());
-    CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+    CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
                                   size_y, rotate, flags,
                                   /*color_scheme=*/nullptr,
                                   /*need_to_restore=*/true, /*pause=*/nullptr);
@@ -575,18 +575,18 @@
 
   RetainPtr<CFX_DIBitmap> pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
   // Create will probably work fine even if it fails here: we will just attach
-  // a zero-sized bitmap to |pDevice|.
+  // a zero-sized bitmap to `device`.
   pBitmap->Create(size_x, size_y, FXDIB_Format::kArgb);
   pBitmap->Clear(0x00ffffff);
-  CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
-  pContext->m_pDevice = pdfium::WrapUnique(pDevice);
-  pDevice->Attach(pBitmap);
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  device->Attach(pBitmap);
+  context->m_pDevice = std::move(device);
   if (bHasMask) {
-    pContext->m_pOptions = std::make_unique<CPDF_RenderOptions>();
-    pContext->m_pOptions->GetOptions().bBreakForMasks = true;
+    context->m_pOptions = std::make_unique<CPDF_RenderOptions>();
+    context->m_pOptions->GetOptions().bBreakForMasks = true;
   }
 
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+  CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
                                 size_y, rotate, flags, /*color_scheme=*/nullptr,
                                 /*need_to_restore=*/true,
                                 /*pause=*/nullptr);
@@ -597,8 +597,8 @@
     if (win_dc.GetDeviceType() == DeviceType::kPrinter) {
       auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
       if (pDst->Create(size_x, size_y, FXDIB_Format::kRgb32)) {
-        fxcrt::spanset(pDst->GetBuffer().first(pBitmap->GetPitch() * size_y),
-                       -1);
+        fxcrt::spanset(
+            pDst->GetWritableBuffer().first(pBitmap->GetPitch() * size_y), -1);
         pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
                               BlendMode::kNormal, nullptr, false);
         win_dc.StretchDIBits(pDst, 0, 0, size_x, size_y);
@@ -618,21 +618,21 @@
   for (size_t i = 0; i < mask_boxes.size(); i++) {
     bitmaps[i] = GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate,
                                pBitmap, mask_boxes[i], &bitmap_areas[i]);
-    pContext->m_pRenderer->Continue(nullptr);
+    context->m_pRenderer->Continue(nullptr);
   }
 
   // Begin rendering to the printer. Add flag to indicate the renderer should
   // pause after each image mask.
   pPage->ClearRenderContext();
-  pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
-  pContext = pOwnedContext.get();
-  pPage->SetRenderContext(std::move(pOwnedContext));
-  pContext->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
+  owned_context = std::make_unique<CPDF_PageRenderContext>();
+  context = owned_context.get();
+  pPage->SetRenderContext(std::move(owned_context));
+  context->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
       dc, render_data->GetPSFontTracker());
-  pContext->m_pOptions = std::make_unique<CPDF_RenderOptions>();
-  pContext->m_pOptions->GetOptions().bBreakForMasks = true;
+  context->m_pOptions = std::make_unique<CPDF_RenderOptions>();
+  context->m_pOptions->GetOptions().bBreakForMasks = true;
 
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+  CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
                                 size_y, rotate, flags, /*color_scheme=*/nullptr,
                                 /*need_to_restore=*/true,
                                 /*pause=*/nullptr);
@@ -641,10 +641,10 @@
   for (size_t i = 0; i < mask_boxes.size(); i++) {
     // Render the bitmap for the mask and free the bitmap.
     if (bitmaps[i]) {  // will be null if mask has zero area
-      RenderBitmap(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
+      RenderBitmap(context->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
     }
     // Render the next portion of page.
-    pContext->m_pRenderer->Continue(nullptr);
+    context->m_pRenderer->Continue(nullptr);
   }
 }
 #endif  // BUILDFLAG(IS_WIN)
@@ -664,18 +664,17 @@
   if (!pPage)
     return;
 
-  auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  auto owned_context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* context = owned_context.get();
   CPDF_Page::RenderContextClearer clearer(pPage);
-  pPage->SetRenderContext(std::move(pOwnedContext));
-
-  auto pOwnedDevice = std::make_unique<CFX_DefaultRenderDevice>();
-  CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
-  pContext->m_pDevice = std::move(pOwnedDevice);
+  pPage->SetRenderContext(std::move(owned_context));
 
   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
-  pDevice->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER));
-  CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  device->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER));
+  context->m_pDevice = std::move(device);
+
+  CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
                                 size_y, rotate, flags, /*color_scheme=*/nullptr,
                                 /*need_to_restore=*/true,
                                 /*pause=*/nullptr);
@@ -700,18 +699,16 @@
   if (!pPage)
     return;
 
-  auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
+  auto owned_context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* context = owned_context.get();
   CPDF_Page::RenderContextClearer clearer(pPage);
-  pPage->SetRenderContext(std::move(pOwnedContext));
-
-  auto pOwnedDevice = std::make_unique<CFX_DefaultRenderDevice>();
-  CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
-  pContext->m_pDevice = std::move(pOwnedDevice);
+  pPage->SetRenderContext(std::move(owned_context));
 
   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
-  pDevice->AttachWithRgbByteOrder(std::move(pBitmap),
-                                  !!(flags & FPDF_REVERSE_BYTE_ORDER));
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  device->AttachWithRgbByteOrder(std::move(pBitmap),
+                                 !!(flags & FPDF_REVERSE_BYTE_ORDER));
+  context->m_pDevice = std::move(device);
 
   CFX_FloatRect clipping_rect;
   if (clipping)
@@ -722,41 +719,36 @@
   CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(rect, 0);
   if (matrix)
     transform_matrix *= CFXMatrixFromFSMatrix(*matrix);
-  CPDFSDK_RenderPage(pContext, pPage, transform_matrix, clip_rect, flags,
+  CPDFSDK_RenderPage(context, pPage, transform_matrix, clip_rect, flags,
                      /*color_scheme=*/nullptr);
 }
 
 #if defined(_SKIA_SUPPORT_)
-FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
-                                                           int size_x,
-                                                           int size_y) {
-  auto skDevice = std::make_unique<CFX_DefaultRenderDevice>();
-  std::unique_ptr<SkPictureRecorder> recorder =
-      skDevice->CreateRecorder(SkRect::MakeWH(size_x, size_y));
-
-  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
-  if (!pPage) {
-    // The equivalent bitmap APIs don't signal failure in this case, but defer
-    // the real work to a later call to `FPDF_FFLDraw()`. This is the case for
-    // XFA pages, for example.
-    //
-    // The caller still needs the `SkPictureRecorder` in order to call
-    // `FPDF_FFLRecord()` later.
-    return recorder.release();
+FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageSkia(FPDF_SKIA_CANVAS canvas,
+                                                   FPDF_PAGE page,
+                                                   int size_x,
+                                                   int size_y) {
+  if (!canvas) {
+    return;
   }
 
-  auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
-  pOwnedContext->m_pDevice = std::move(skDevice);
+  CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page);
+  if (!cpdf_page) {
+    return;
+  }
 
-  CPDF_Page::RenderContextClearer clearer(pPage);
-  CPDF_PageRenderContext* pContext = pOwnedContext.get();
-  pPage->SetRenderContext(std::move(pOwnedContext));
+  auto owned_context = std::make_unique<CPDF_PageRenderContext>();
+  CPDF_PageRenderContext* context = owned_context.get();
+  CPDF_Page::RenderContextClearer clearer(cpdf_page);
+  cpdf_page->SetRenderContext(std::move(owned_context));
 
-  CPDFSDK_RenderPageWithContext(pContext, pPage, 0, 0, size_x, size_y, 0, 0,
+  auto device = std::make_unique<CFX_DefaultRenderDevice>();
+  device->AttachCanvas(reinterpret_cast<SkCanvas*>(canvas));
+  context->m_pDevice = std::move(device);
+
+  CPDFSDK_RenderPageWithContext(context, cpdf_page, 0, 0, size_x, size_y, 0, 0,
                                 /*color_scheme=*/nullptr,
                                 /*need_to_restore=*/true, /*pause=*/nullptr);
-
-  return recorder.release();
 }
 #endif  // defined(_SKIA_SUPPORT_)
 
@@ -919,7 +911,7 @@
 }
 
 FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
-  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer().data()
+  return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWritableBuffer().data()
                 : nullptr;
 }
 
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index b66fe62..5907a4e 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -312,7 +312,7 @@
     CHK(FPDFPage_HasFormFieldAtPoint);
     CHK(FPDF_FFLDraw);
 #if defined(_SKIA_SUPPORT_)
-    CHK(FPDF_FFLRecord);
+    CHK(FPDF_FFLDrawSkia);
 #endif
     CHK(FPDF_GetFormType);
     CHK(FPDF_LoadXFA);
@@ -518,7 +518,7 @@
     CHK(FPDF_RenderPageBitmap);
     CHK(FPDF_RenderPageBitmapWithMatrix);
 #if defined(_SKIA_SUPPORT_)
-    CHK(FPDF_RenderPageSkp);
+    CHK(FPDF_RenderPageSkia);
 #endif
 #if defined(_WIN32)
     CHK(FPDF_SetPrintMode);
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index 469e9bd..06b337e 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -133,7 +133,7 @@
 ScopedFPDFBitmap SkPictureToPdfiumBitmap(sk_sp<SkPicture> picture,
                                          const SkISize& size) {
   sk_sp<SkSurface> surface =
-      SkSurface::MakeRasterN32Premul(size.width(), size.height());
+      SkSurfaces::Raster(SkImageInfo::MakeN32Premul(size));
   if (!surface) {
     ADD_FAILURE() << "Could not create SkSurface";
     return nullptr;
@@ -232,14 +232,17 @@
     int width = static_cast<int>(FPDF_GetPageWidth(page));
     int height = static_cast<int>(FPDF_GetPageHeight(page));
 
-    FPDF_RECORDER opaque_recorder = FPDF_RenderPageSkp(page, width, height);
-    ASSERT_TRUE(opaque_recorder);
+    sk_sp<SkPicture> picture;
+    {
+      auto recorder = std::make_unique<SkPictureRecorder>();
+      recorder->beginRecording(width, height);
 
-    SkPictureRecorder* recorder =
-        reinterpret_cast<SkPictureRecorder*>(opaque_recorder);
-    sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
-    delete recorder;
-    ASSERT_TRUE(picture);
+      FPDF_RenderPageSkia(
+          reinterpret_cast<FPDF_SKIA_CANVAS>(recorder->getRecordingCanvas()),
+          page, width, height);
+      picture = recorder->finishRecordingAsPicture();
+      ASSERT_TRUE(picture);
+    }
 
     ScopedFPDFBitmap bitmap = SkPictureToPdfiumBitmap(
         std::move(picture), SkISize::Make(width, height));
@@ -1454,11 +1457,14 @@
   // macOS rendering result doesn't.
 
   const char* original_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "29cb8045c21cfa2c920fdf43de70efd8";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "0e339d606aafb63077f49e238dc27cb0";
-#endif
+    return "0e339d606aafb63077f49e238dc27cb0";
+#else
     return "288502887ffc63291f35a0573b944375";
+#endif
   }();
   static const char kNoNativeTextChecksum[] =
       "288502887ffc63291f35a0573b944375";
@@ -1473,6 +1479,27 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFViewEmbedderTest, RenderAnnotationWithPrintingFlag) {
+  const char* annotation_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "eaece6b8041c0cb9b33398e5b6d5ddda";
+    }
+    return "c108ba6e0a9743652f12e4bc223f9b32";
+  }();
+  static const char kPrintingChecksum[] = "3e235b9f88f652f2b97b1fc393924849";
+  ASSERT_TRUE(OpenDocument("bug_1658.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // A yellow highlight is rendered with `FPDF_ANNOT` flag.
+  TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, annotation_checksum);
+
+  // After adding `FPDF_PRINTING` flag, the yellow highlight is not rendered.
+  TestRenderPageBitmapWithFlags(page, FPDF_PRINTING | FPDF_ANNOT,
+                                kPrintingChecksum);
+  UnloadPage(page);
+}
+
 // TODO(crbug.com/pdfium/1955): Remove this test once pixel tests can pass with
 // `reverse-byte-order` option.
 TEST_F(FPDFViewEmbedderTest, RenderBlueAndRedImagesWithReverByteOrderFlag) {
@@ -1628,7 +1655,7 @@
 
   const char* lcd_text_checksum = []() {
     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "c1c548442e0e0f949c5550d89bf8ae3b";
+      return "d1decde2de1c07b5274cc8cb44f92427";
 #if BUILDFLAG(IS_APPLE)
     return "6eef7237f7591f07616e238422086737";
 #else
@@ -1636,11 +1663,14 @@
 #endif  // BUILDFLAG(IS_APPLE)
   }();
   const char* no_smoothtext_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "cd5bbe9407c3fcc85d365172a9a55abd";
+    }
 #if BUILDFLAG(IS_APPLE)
-    if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "6eef7237f7591f07616e238422086737";
-#endif
+    return "6eef7237f7591f07616e238422086737";
+#else
     return "37d0b34e1762fdda4c05ce7ea357b828";
+#endif
   }();
 
   TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, lcd_text_checksum);
@@ -2038,7 +2068,15 @@
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT,
-                                "4ef1f65ab1ac76acb97a3540dcb10b4e");
+  const char* checksum = []() {
+#if !BUILDFLAG(IS_APPLE)
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "ceeb93d2bcdb586d62c95b33cadcd873";
+    }
+#endif
+    return "4ef1f65ab1ac76acb97a3540dcb10b4e";
+  }();
+
+  TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, checksum);
   UnloadPage(page);
 }
diff --git a/fpdfsdk/pwl/cpwl_caret.cpp b/fpdfsdk/pwl/cpwl_caret.cpp
index fe29388..47f7864 100644
--- a/fpdfsdk/pwl/cpwl_caret.cpp
+++ b/fpdfsdk/pwl/cpwl_caret.cpp
@@ -76,7 +76,7 @@
       return;
 
     m_pTimer.reset();
-    CPWL_Wnd::SetVisible(false);
+    (void)CPWL_Wnd::SetVisible(false);
     // Note, |this| may no longer be viable at this point. If more work needs
     // to be done, check the return value of SetVisible().
     return;
diff --git a/fpdfsdk/pwl/cpwl_cblistbox.cpp b/fpdfsdk/pwl/cpwl_cblistbox.cpp
index 492b6ae..0771b26 100644
--- a/fpdfsdk/pwl/cpwl_cblistbox.cpp
+++ b/fpdfsdk/pwl/cpwl_cblistbox.cpp
@@ -75,7 +75,6 @@
       break;
     default:
       NOTREACHED_NORETURN();
-      break;
   }
   return OnNotifySelectionChanged(true, nFlag);
 }
diff --git a/fpdfsdk/pwl/cpwl_combo_box.cpp b/fpdfsdk/pwl/cpwl_combo_box.cpp
index c84b7c0..7c122d7 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.cpp
+++ b/fpdfsdk/pwl/cpwl_combo_box.cpp
@@ -202,8 +202,8 @@
   m_pList->Realize();
 }
 
-bool CPWL_ComboBox::RePosChildWnd() {
-  ObservedPtr<CPWL_ComboBox> thisObserved(this);
+bool CPWL_ComboBox::RepositionChildWnd() {
+  ObservedPtr<CPWL_ComboBox> this_observed(this);
   const CFX_FloatRect rcClient = GetClientRect();
   if (m_bPopup) {
     const float fOldWindowHeight = m_rcOldWindow.Height();
@@ -227,26 +227,31 @@
 
     if (m_pButton) {
       m_pButton->Move(rcButton, true, false);
-      if (!thisObserved)
+      if (!this_observed) {
         return false;
+      }
     }
 
     if (m_pEdit) {
       m_pEdit->Move(rcEdit, true, false);
-      if (!thisObserved)
+      if (!this_observed) {
         return false;
+      }
     }
 
     if (m_pList) {
-      if (!m_pList->SetVisible(true) || !thisObserved)
+      if (!m_pList->SetVisible(true) || !this_observed) {
         return false;
+      }
 
-      if (!m_pList->Move(rcList, true, false) || !thisObserved)
+      if (!m_pList->Move(rcList, true, false) || !this_observed) {
         return false;
+      }
 
       m_pList->ScrollToListItem(m_nSelectItem);
-      if (!thisObserved)
+      if (!this_observed) {
         return false;
+      }
     }
     return true;
   }
@@ -256,8 +261,9 @@
 
   if (m_pButton) {
     m_pButton->Move(rcButton, true, false);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   CFX_FloatRect rcEdit = rcClient;
@@ -265,14 +271,19 @@
 
   if (m_pEdit) {
     m_pEdit->Move(rcEdit, true, false);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   if (m_pList) {
-    m_pList->SetVisible(false);
-    if (!thisObserved)
+    if (!m_pList->SetVisible(false)) {
+      m_pList = nullptr;  // Gone, dangling even.
       return false;
+    }
+    if (!this_observed) {
+      return false;
+    }
   }
 
   return true;
@@ -301,11 +312,12 @@
     return Move(m_rcOldWindow, true, true);
   }
 
-  ObservedPtr<CPWL_ComboBox> thisObserved(this);
+  ObservedPtr<CPWL_ComboBox> this_observed(this);
   if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), {}))
-    return !!thisObserved;
-  if (!thisObserved)
+    return !!this_observed;
+  if (!this_observed) {
     return false;
+  }
 
   float fBorderWidth = m_pList->GetBorderWidth() * 2;
   float fPopupMin = 0.0f;
@@ -334,7 +346,7 @@
     return false;
 
   GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), {});
-  return !!thisObserved;
+  return !!this_observed;
 }
 
 bool CPWL_ComboBox::OnKeyDown(FWL_VKEYCODE nKeyCode,
@@ -344,22 +356,22 @@
   if (!m_pEdit)
     return false;
 
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   m_nSelectItem = -1;
 
   switch (nKeyCode) {
     case FWL_VKEY_Up:
       if (m_pList->GetCurSel() > 0) {
         if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) ||
-            !thisObserved) {
+            !this_observed) {
           return false;
         }
         if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) ||
-            !thisObserved) {
+            !this_observed) {
           return false;
         }
         if (m_pList->IsMovementKey(nKeyCode)) {
-          if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !thisObserved) {
+          if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !this_observed) {
             return false;
           }
           SetSelectText();
@@ -369,15 +381,15 @@
     case FWL_VKEY_Down:
       if (m_pList->GetCurSel() < m_pList->GetCount() - 1) {
         if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) ||
-            !thisObserved) {
+            !this_observed) {
           return false;
         }
         if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) ||
-            !thisObserved) {
+            !this_observed) {
           return false;
         }
         if (m_pList->IsMovementKey(nKeyCode)) {
-          if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !thisObserved) {
+          if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !this_observed) {
             return false;
           }
           SetSelectText();
@@ -431,13 +443,13 @@
   if (HasFlag(PCBS_ALLOWCUSTOMTEXT))
     return m_pEdit->OnChar(nChar, nFlag);
 
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) ||
-      !thisObserved) {
+      !this_observed) {
     return false;
   }
   if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) ||
-      !thisObserved) {
+      !this_observed) {
     return false;
   }
   if (!m_pList->IsChar(nChar, nFlag))
diff --git a/fpdfsdk/pwl/cpwl_combo_box.h b/fpdfsdk/pwl/cpwl_combo_box.h
index 87749fa..45729ae 100644
--- a/fpdfsdk/pwl/cpwl_combo_box.h
+++ b/fpdfsdk/pwl/cpwl_combo_box.h
@@ -34,7 +34,7 @@
   void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) override;
   void CreateChildWnd(const CreateParams& cp) override;
-  bool RePosChildWnd() override;
+  bool RepositionChildWnd() override;
   CFX_FloatRect GetFocusRect() const override;
   void SetFocus() override;
   void KillFocus() override;
diff --git a/fpdfsdk/pwl/cpwl_edit.cpp b/fpdfsdk/pwl/cpwl_edit.cpp
index e41b339..3657b88 100644
--- a/fpdfsdk/pwl/cpwl_edit.cpp
+++ b/fpdfsdk/pwl/cpwl_edit.cpp
@@ -46,17 +46,18 @@
   m_pEditImpl->Paint();
 }
 
-bool CPWL_Edit::RePosChildWnd() {
+bool CPWL_Edit::RepositionChildWnd() {
   if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
     CFX_FloatRect rcWindow = m_rcOldWindow;
     CFX_FloatRect rcVScroll =
         CFX_FloatRect(rcWindow.right, rcWindow.bottom,
                       rcWindow.right + CPWL_ScrollBar::kWidth, rcWindow.top);
 
-    ObservedPtr<CPWL_Edit> thisObserved(this);
+    ObservedPtr<CPWL_Edit> this_observed(this);
     pVSB->Move(rcVScroll, true, false);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   if (m_pCaret && !HasFlag(PES_TEXTOVERFLOW)) {
@@ -237,12 +238,15 @@
   ObservedPtr<CPWL_Edit> observed_ptr(this);
   CPWL_ScrollBar* pScroll = GetVScrollBar();
   if (pScroll && pScroll->IsVisible()) {
-    pScroll->SetVisible(false);
-    if (!observed_ptr)
+    if (!pScroll->SetVisible(false)) {
       return;
-
-    if (!Move(m_rcOldWindow, true, true))
+    }
+    if (!observed_ptr) {
       return;
+    }
+    if (!Move(m_rcOldWindow, true, true)) {
+      return;
+    }
   }
 
   m_pEditImpl->SelectNone();
@@ -342,7 +346,7 @@
     if (nSelStart == nSelEnd)
       nSelEnd = nSelStart + 1;
 
-    ObservedPtr<CPWL_Wnd> thisObserved(this);
+    ObservedPtr<CPWL_Wnd> this_observed(this);
 
     bool bRC;
     bool bExit;
@@ -350,8 +354,9 @@
         GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true,
         nFlag);
 
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
 
     if (!bRC)
       return false;
@@ -423,15 +428,16 @@
         break;
     }
 
-    ObservedPtr<CPWL_Wnd> thisObserved(this);
+    ObservedPtr<CPWL_Wnd> this_observed(this);
 
     WideString strChangeEx;
     std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
         GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true,
         nFlag);
 
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   if (!bRC)
@@ -785,10 +791,11 @@
   if (!IsFocused() || m_pEditImpl->IsSelected())
     bVisible = false;
 
-  ObservedPtr<CPWL_Edit> thisObserved(this);
+  ObservedPtr<CPWL_Edit> this_observed(this);
   m_pCaret->SetCaret(bVisible, ptHead, ptFoot);
-  if (!thisObserved)
+  if (!this_observed) {
     return false;
+  }
 
   return true;
 }
diff --git a/fpdfsdk/pwl/cpwl_edit.h b/fpdfsdk/pwl/cpwl_edit.h
index 696e32c..5e2e5fc 100644
--- a/fpdfsdk/pwl/cpwl_edit.h
+++ b/fpdfsdk/pwl/cpwl_edit.h
@@ -33,7 +33,7 @@
   ~CPWL_Edit() override;
 
   // CPWL_Wnd:
-  bool RePosChildWnd() override;
+  bool RepositionChildWnd() override;
   CFX_FloatRect GetClientRect() const override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp
index 50e2c42..37ec306 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp
@@ -1232,9 +1232,12 @@
       if (!m_bNotifyFlag) {
         AutoRestorer<bool> restorer(&m_bNotifyFlag);
         m_bNotifyFlag = true;
-        if (std::vector<CFX_FloatRect>* pRects = m_Refresh.GetRefreshRects()) {
-          for (auto& rect : *pRects)
-            m_pNotify->InvalidateRect(&rect);
+        std::vector<CFX_FloatRect>* pRects = m_Refresh.GetRefreshRects();
+        for (auto& rect : *pRects) {
+          if (!m_pNotify->InvalidateRect(&rect)) {
+            m_pNotify = nullptr;  // Gone, dangling even.
+            break;
+          }
         }
       }
     }
@@ -1302,7 +1305,9 @@
           AutoRestorer<bool> restorer(&m_bNotifyFlag);
           m_bNotifyFlag = true;
           CFX_FloatRect rcRefresh = VTToEdit(rcWord);
-          m_pNotify->InvalidateRect(&rcRefresh);
+          if (!m_pNotify->InvalidateRect(&rcRefresh)) {
+            m_pNotify = nullptr;  // Gone, dangling even.
+          }
         }
       }
     } else {
@@ -1316,7 +1321,9 @@
           AutoRestorer<bool> restorer(&m_bNotifyFlag);
           m_bNotifyFlag = true;
           CFX_FloatRect rcRefresh = VTToEdit(rcLine);
-          m_pNotify->InvalidateRect(&rcRefresh);
+          if (!m_pNotify->InvalidateRect(&rcRefresh)) {
+            m_pNotify = nullptr;  // Gone, dangling even.
+          }
         }
       }
 
diff --git a/fpdfsdk/pwl/cpwl_list_box.cpp b/fpdfsdk/pwl/cpwl_list_box.cpp
index 083e240..2203ca9 100644
--- a/fpdfsdk/pwl/cpwl_list_box.cpp
+++ b/fpdfsdk/pwl/cpwl_list_box.cpp
@@ -194,9 +194,10 @@
   m_pListCtrl->SetScrollPos(CFX_PointF(0, pos));
 }
 
-bool CPWL_ListBox::RePosChildWnd() {
-  if (!CPWL_Wnd::RePosChildWnd())
+bool CPWL_ListBox::RepositionChildWnd() {
+  if (!CPWL_Wnd::RepositionChildWnd()) {
     return false;
+  }
 
   m_pListCtrl->SetPlateRect(GetListRect());
   return true;
@@ -204,7 +205,7 @@
 
 bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown,
                                             Mask<FWL_EVENTFLAG> nFlag) {
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
 
   WideString swChange = GetText();
   WideString strChangeEx;
@@ -216,8 +217,9 @@
       GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown,
       nFlag);
 
-  if (!thisObserved)
+  if (!this_observed) {
     return false;
+  }
 
   return bExit;
 }
@@ -270,15 +272,13 @@
                           Info.fContentMax - Info.fContentMin) ||
       FXSYS_IsFloatEqual(Info.fPlateWidth,
                          Info.fContentMax - Info.fContentMin)) {
-    if (pScroll->IsVisible()) {
-      pScroll->SetVisible(false);
-      RePosChildWnd();
+    if (pScroll->IsVisible() && pScroll->SetVisible(false)) {
+      RepositionChildWnd();
     }
-  } else {
-    if (!pScroll->IsVisible()) {
-      pScroll->SetVisible(true);
-      RePosChildWnd();
-    }
+    return;
+  }
+  if (!pScroll->IsVisible() && pScroll->SetVisible(true)) {
+    RepositionChildWnd();
   }
 }
 
@@ -286,8 +286,8 @@
   SetScrollPosition(fy);
 }
 
-void CPWL_ListBox::OnInvalidateRect(const CFX_FloatRect& rect) {
-  InvalidateRect(&rect);
+bool CPWL_ListBox::OnInvalidateRect(const CFX_FloatRect& rect) {
+  return InvalidateRect(&rect);
 }
 
 void CPWL_ListBox::Select(int32_t nItemIndex) {
diff --git a/fpdfsdk/pwl/cpwl_list_box.h b/fpdfsdk/pwl/cpwl_list_box.h
index 49d9363..85b0077 100644
--- a/fpdfsdk/pwl/cpwl_list_box.h
+++ b/fpdfsdk/pwl/cpwl_list_box.h
@@ -39,7 +39,7 @@
   void SetScrollInfo(const PWL_SCROLL_INFO& info) override;
   void SetScrollPosition(float pos) override;
   void ScrollWindowVertically(float pos) override;
-  bool RePosChildWnd() override;
+  bool RepositionChildWnd() override;
   CFX_FloatRect GetFocusRect() const override;
   void SetFontSize(float fFontSize) override;
   float GetFontSize() const override;
@@ -52,7 +52,7 @@
                         float fSmallStep,
                         float fBigStep) override;
   void OnSetScrollPosY(float fy) override;
-  void OnInvalidateRect(const CFX_FloatRect& pRect) override;
+  [[nodiscard]] bool OnInvalidateRect(const CFX_FloatRect& pRect) override;
 
   bool OnNotifySelectionChanged(bool bKeyDown, Mask<FWL_EVENTFLAG> nFlag);
 
diff --git a/fpdfsdk/pwl/cpwl_list_ctrl.cpp b/fpdfsdk/pwl/cpwl_list_ctrl.cpp
index 046e772..d7e3b15 100644
--- a/fpdfsdk/pwl/cpwl_list_ctrl.cpp
+++ b/fpdfsdk/pwl/cpwl_list_ctrl.cpp
@@ -356,26 +356,30 @@
 }
 
 void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) {
-  if (m_pNotify) {
-    if (nItemIndex == -1) {
-      if (!m_bNotifyFlag) {
-        m_bNotifyFlag = true;
-        CFX_FloatRect rcRefresh = m_rcPlate;
-        m_pNotify->OnInvalidateRect(rcRefresh);
-        m_bNotifyFlag = false;
+  if (!m_pNotify) {
+    return;
+  }
+  if (nItemIndex == -1) {
+    if (!m_bNotifyFlag) {
+      m_bNotifyFlag = true;
+      CFX_FloatRect rcRefresh = m_rcPlate;
+      if (!m_pNotify->OnInvalidateRect(rcRefresh)) {
+        m_pNotify = nullptr;  // Gone, dangling even.
       }
-    } else {
-      if (!m_bNotifyFlag) {
-        m_bNotifyFlag = true;
-        CFX_FloatRect rcRefresh = GetItemRect(nItemIndex);
-        rcRefresh.left -= 1.0f;
-        rcRefresh.right += 1.0f;
-        rcRefresh.bottom -= 1.0f;
-        rcRefresh.top += 1.0f;
-
-        m_pNotify->OnInvalidateRect(rcRefresh);
-        m_bNotifyFlag = false;
+      m_bNotifyFlag = false;
+    }
+  } else {
+    if (!m_bNotifyFlag) {
+      m_bNotifyFlag = true;
+      CFX_FloatRect rcRefresh = GetItemRect(nItemIndex);
+      rcRefresh.left -= 1.0f;
+      rcRefresh.right += 1.0f;
+      rcRefresh.bottom -= 1.0f;
+      rcRefresh.top += 1.0f;
+      if (!m_pNotify->OnInvalidateRect(rcRefresh)) {
+        m_pNotify = nullptr;  // Gone, dangling even.
       }
+      m_bNotifyFlag = false;
     }
   }
 }
diff --git a/fpdfsdk/pwl/cpwl_list_ctrl.h b/fpdfsdk/pwl/cpwl_list_ctrl.h
index 3f36354..c278b81 100644
--- a/fpdfsdk/pwl/cpwl_list_ctrl.h
+++ b/fpdfsdk/pwl/cpwl_list_ctrl.h
@@ -31,7 +31,9 @@
                                   float fSmallStep,
                                   float fBigStep) = 0;
     virtual void OnSetScrollPosY(float fy) = 0;
-    virtual void OnInvalidateRect(const CFX_FloatRect& rect) = 0;
+
+    // Returns true if `this` is still allocated.
+    [[nodiscard]] virtual bool OnInvalidateRect(const CFX_FloatRect& rect) = 0;
   };
 
   CPWL_ListCtrl();
diff --git a/fpdfsdk/pwl/cpwl_sbbutton.cpp b/fpdfsdk/pwl/cpwl_sbbutton.cpp
index 889ce5e..28f037a 100644
--- a/fpdfsdk/pwl/cpwl_sbbutton.cpp
+++ b/fpdfsdk/pwl/cpwl_sbbutton.cpp
@@ -45,23 +45,23 @@
                         nTransparency, 80, 220);
     // draw arrow
     if (rectWnd.top - rectWnd.bottom > 6.0f) {
-      float fX = rectWnd.left + 1.5f;
-      float fY = rectWnd.bottom;
       std::vector<CFX_PointF> pts;
-      static constexpr float kOffsetsX[] = {2.5f, 2.5f, 4.5f, 6.5f,
-                                            6.5f, 4.5f, 2.5f};
-      static constexpr float kOffsetsY[] = {5.0f, 6.0f, 4.0f, 6.0f,
-                                            5.0f, 3.0f, 5.0f};
-      static constexpr float kOffsetsMinY[] = {4.0f, 3.0f, 5.0f, 3.0f,
-                                               4.0f, 6.0f, 4.0f};
-      static_assert(std::size(kOffsetsX) == std::size(kOffsetsY),
-                    "Wrong offset count");
-      static_assert(std::size(kOffsetsX) == std::size(kOffsetsMinY),
-                    "Wrong offset count");
-      const float* pOffsetsY =
-          m_eSBButtonType == Type::kMinButton ? kOffsetsMinY : kOffsetsY;
-      for (size_t i = 0; i < std::size(kOffsetsX); ++i)
-        pts.push_back(CFX_PointF(fX + kOffsetsX[i], fY + pOffsetsY[i]));
+      CFX_PointF origin(rectWnd.left + 1.5f, rectWnd.bottom);
+      if (m_eSBButtonType == Type::kMinButton) {
+        static constexpr CFX_PointF kOffsetsMin[] = {
+            {2.5f, 4.0f}, {2.5f, 3.0f}, {4.5f, 5.0f}, {6.5f, 3.0f},
+            {6.5f, 4.0f}, {4.5f, 6.0f}, {2.5f, 4.0f}};
+        for (const auto& offset : kOffsetsMin) {
+          pts.push_back(origin + offset);
+        }
+      } else {
+        static constexpr CFX_PointF kOffsets[] = {
+            {2.5f, 5.0f}, {2.5f, 6.0f}, {4.5f, 4.0f}, {6.5f, 6.0f},
+            {6.5f, 5.0f}, {4.5f, 3.0f}, {2.5f, 5.0f}};
+        for (const auto& offset : kOffsets) {
+          pts.push_back(origin + offset);
+        }
+      }
       pDevice->DrawFillArea(mtUser2Device, pts,
                             ArgbEncode(nTransparency, 255, 255, 255));
     }
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.cpp b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
index 00a9418..67c503c 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.cpp
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.cpp
@@ -125,7 +125,7 @@
   CPWL_Wnd::OnDestroy();
 }
 
-bool CPWL_ScrollBar::RePosChildWnd() {
+bool CPWL_ScrollBar::RepositionChildWnd() {
   CFX_FloatRect rcClient = GetClientRect();
   CFX_FloatRect rcMinButton;
   CFX_FloatRect rcMaxButton;
@@ -149,16 +149,18 @@
     }
   }
 
-  ObservedPtr<CPWL_ScrollBar> thisObserved(this);
+  ObservedPtr<CPWL_ScrollBar> this_observed(this);
   if (m_pMinButton) {
     m_pMinButton->Move(rcMinButton, true, false);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
   if (m_pMaxButton) {
     m_pMaxButton->Move(rcMaxButton, true, false);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   return MovePosButton(false);
@@ -313,8 +315,8 @@
     auto pButton = std::make_unique<CPWL_SBButton>(
         scp, CloneAttachedData(), CPWL_SBButton::Type::kPosButton);
     m_pPosButton = pButton.get();
-    ObservedPtr<CPWL_ScrollBar> thisObserved(this);
-    if (m_pPosButton->SetVisible(false) && thisObserved) {
+    ObservedPtr<CPWL_ScrollBar> this_observed(this);
+    if (m_pPosButton->SetVisible(false) && this_observed) {
       AddChild(std::move(pButton));
       m_pPosButton->Realize();
     }
@@ -331,21 +333,22 @@
   if (!m_pPosButton)
     return;
 
-  ObservedPtr<CPWL_ScrollBar> thisObserved(this);
+  ObservedPtr<CPWL_ScrollBar> this_observed(this);
   m_sData.SetScrollRange(fMin, fMax);
   m_sData.SetClientWidth(fClientWidth);
 
   if (FXSYS_IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
-    m_pPosButton->SetVisible(false);
+    (void)m_pPosButton->SetVisible(false);
     // Note, |this| may no longer be viable at this point. If more work needs
-    // to be done, check thisObserved.
+    // to be done, check this_observed.
     return;
   }
 
-  if (!m_pPosButton->SetVisible(true) || !thisObserved)
+  if (!m_pPosButton->SetVisible(true) || !this_observed) {
     return;
+  }
 
-  MovePosButton(true);
+  (void)MovePosButton(true);
   // Note, |this| may no longer be viable at this point. If more work needs
   // to be done, check the return value of MovePosButton().
 }
@@ -354,7 +357,7 @@
   float fOldPos = m_sData.fScrollPos;
   m_sData.SetPos(fPos);
   if (!FXSYS_IsFloatEqual(m_sData.fScrollPos, fOldPos)) {
-    MovePosButton(true);
+    (void)MovePosButton(true);
     // Note, |this| may no longer be viable at this point. If more work needs
     // to be done, check the return value of MovePosButton().
   }
@@ -385,10 +388,11 @@
     CFX_FloatRect rcPosButton =
         CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop);
 
-    ObservedPtr<CPWL_ScrollBar> thisObserved(this);
+    ObservedPtr<CPWL_ScrollBar> this_observed(this);
     m_pPosButton->Move(rcPosButton, true, bRefresh);
-    if (!thisObserved)
+    if (!this_observed) {
       return false;
+    }
   }
 
   return true;
diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.h b/fpdfsdk/pwl/cpwl_scroll_bar.h
index 6cca8fb..91a7516 100644
--- a/fpdfsdk/pwl/cpwl_scroll_bar.h
+++ b/fpdfsdk/pwl/cpwl_scroll_bar.h
@@ -101,7 +101,7 @@
 
   // CPWL_Wnd:
   void OnDestroy() override;
-  bool RePosChildWnd() override;
+  bool RepositionChildWnd() override;
   void DrawThisAppearance(CFX_RenderDevice* pDevice,
                           const CFX_Matrix& mtUser2Device) override;
   bool OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
@@ -124,7 +124,7 @@
   void SetScrollPos(float fPos);
 
   // Returns |true| iff this instance is still allocated.
-  bool MovePosButton(bool bRefresh);
+  [[nodiscard]] bool MovePosButton(bool bRefresh);
   void SetScrollStep(float fBigStep, float fSmallStep);
   void NotifyScrollWindow();
   CFX_FloatRect GetScrollArea() const;
diff --git a/fpdfsdk/pwl/cpwl_wnd.cpp b/fpdfsdk/pwl/cpwl_wnd.cpp
index 755fcbb..a2dafdb 100644
--- a/fpdfsdk/pwl/cpwl_wnd.cpp
+++ b/fpdfsdk/pwl/cpwl_wnd.cpp
@@ -46,14 +46,16 @@
 
 CPWL_Wnd::CreateParams::~CreateParams() = default;
 
-class CPWL_MsgControl final : public Observable {
+// For a compound window (a window containing a child window as occurs in a
+// list box, combo box, or even a scroll bar), this class contains information
+// shared amongst the parent and children.
+class CPWL_Wnd::SharedCaptureFocusState final : public Observable {
  public:
-  explicit CPWL_MsgControl(const CPWL_Wnd* pWnd) : m_pCreatedWnd(pWnd) {}
-  ~CPWL_MsgControl() = default;
+  explicit SharedCaptureFocusState(const CPWL_Wnd* pOwnerWnd)
+      : m_pOwnerWnd(pOwnerWnd) {}
+  ~SharedCaptureFocusState() = default;
 
-  bool IsWndCreated(const CPWL_Wnd* pWnd) const {
-    return m_pCreatedWnd == pWnd;
-  }
+  bool IsOwnedByWnd(const CPWL_Wnd* pWnd) const { return m_pOwnerWnd == pWnd; }
 
   bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const {
     return pWnd && pdfium::Contains(m_MousePaths, pWnd);
@@ -67,6 +69,9 @@
     return pWnd && pdfium::Contains(m_KeyboardPaths, pWnd);
   }
 
+  void SetCapture(CPWL_Wnd* pWnd) { m_MousePaths = pWnd->GetAncestors(); }
+  void ReleaseCapture() { m_MousePaths.clear(); }
+
   void SetFocus(CPWL_Wnd* pWnd) {
     m_KeyboardPaths = pWnd->GetAncestors();
     m_pMainKeyboardWnd = pWnd;
@@ -75,8 +80,8 @@
     pWnd->OnSetFocus();
   }
 
-  void KillFocus() {
-    ObservedPtr<CPWL_MsgControl> observed_ptr(this);
+  void ReleaseFocus() {
+    ObservedPtr<SharedCaptureFocusState> observed_ptr(this);
     if (!m_KeyboardPaths.empty()) {
       CPWL_Wnd* pWnd = m_KeyboardPaths.front();
       if (pWnd)
@@ -89,15 +94,29 @@
     m_KeyboardPaths.clear();
   }
 
-  void SetCapture(CPWL_Wnd* pWnd) { m_MousePaths = pWnd->GetAncestors(); }
-
-  void ReleaseCapture() { m_MousePaths.clear(); }
+  void RemoveWnd(CPWL_Wnd* pWnd) {
+    if (pWnd == m_pOwnerWnd) {
+      m_pOwnerWnd = nullptr;
+    }
+    if (pWnd == m_pMainKeyboardWnd) {
+      m_pMainKeyboardWnd = nullptr;
+    }
+    auto mouse_it = std::find(m_MousePaths.begin(), m_MousePaths.end(), pWnd);
+    if (mouse_it != m_MousePaths.end()) {
+      m_MousePaths.erase(mouse_it);
+    }
+    auto keyboard_it =
+        std::find(m_KeyboardPaths.begin(), m_KeyboardPaths.end(), pWnd);
+    if (keyboard_it != m_KeyboardPaths.end()) {
+      m_KeyboardPaths.erase(keyboard_it);
+    }
+  }
 
  private:
+  UnownedPtr<const CPWL_Wnd> m_pOwnerWnd;
+  UnownedPtr<const CPWL_Wnd> m_pMainKeyboardWnd;
   std::vector<UnownedPtr<CPWL_Wnd>> m_MousePaths;
   std::vector<UnownedPtr<CPWL_Wnd>> m_KeyboardPaths;
-  UnownedPtr<const CPWL_Wnd> m_pCreatedWnd;
-  UnownedPtr<const CPWL_Wnd> m_pMainKeyboardWnd;
 };
 
 // static
@@ -148,16 +167,17 @@
     m_rcClip.Inflate(1.0f, 1.0f);
     m_rcClip.Normalize();
   }
-  CreateMsgControl();
+  CreateSharedCaptureFocusState();
 
   CreateParams ccp = m_CreationParams;
   ccp.dwFlags &= 0xFFFF0000L;  // remove sub styles
-  CreateScrollBar(ccp);
+  CreateVScrollBar(ccp);
   CreateChildWnd(ccp);
   m_bVisible = HasFlag(PWS_VISIBLE);
   OnCreated();
-  if (!RePosChildWnd())
+  if (!RepositionChildWnd()) {
     return;
+  }
 
   m_bCreated = true;
 }
@@ -185,7 +205,7 @@
       m_pParent->RemoveChild(this);
     m_bCreated = false;
   }
-  DestroyMsgControl();
+  DestroySharedCaptureFocusState();
 }
 
 bool CPWL_Wnd::Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh) {
@@ -199,8 +219,9 @@
   if (bReset) {
     if (rcOld.left != rcNew.left || rcOld.right != rcNew.right ||
         rcOld.top != rcNew.top || rcOld.bottom != rcNew.bottom) {
-      if (!RePosChildWnd())
+      if (!RepositionChildWnd()) {
         return false;
+      }
     }
   }
   if (bRefresh && !InvalidateRectMove(rcOld, rcNew))
@@ -258,7 +279,7 @@
   if (!IsValid())
     return true;
 
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   CFX_FloatRect rcRefresh = pRect ? *pRect : GetWindowRect();
   if (!HasFlag(PWS_NOREFRESHCLIP)) {
     CFX_FloatRect rcClip = GetClipRect();
@@ -270,7 +291,7 @@
   rcWin.Inflate(1, 1);
   rcWin.Normalize();
   GetFillerNotify()->InvalidateRect(m_pAttachedData.get(), rcWin);
-  return !!thisObserved;
+  return !!this_observed;
 }
 
 bool CPWL_Wnd::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) {
@@ -478,10 +499,6 @@
   return HasFlag(PWS_VSCROLL) ? m_pVScrollBar : nullptr;
 }
 
-void CPWL_Wnd::CreateScrollBar(const CreateParams& cp) {
-  CreateVScrollBar(cp);
-}
-
 void CPWL_Wnd::CreateVScrollBar(const CreateParams& cp) {
   if (m_pVScrollBar || !HasFlag(PWS_VSCROLL))
     return;
@@ -499,30 +516,34 @@
 }
 
 void CPWL_Wnd::SetCapture() {
-  if (CPWL_MsgControl* pMsgCtrl = GetMsgControl())
-    pMsgCtrl->SetCapture(this);
+  if (SharedCaptureFocusState* pSharedState = GetSharedCaptureFocusState()) {
+    pSharedState->SetCapture(this);
+  }
 }
 
 void CPWL_Wnd::ReleaseCapture() {
   for (const auto& pChild : m_Children)
     pChild->ReleaseCapture();
 
-  if (CPWL_MsgControl* pMsgCtrl = GetMsgControl())
-    pMsgCtrl->ReleaseCapture();
+  if (SharedCaptureFocusState* pSharedState = GetSharedCaptureFocusState()) {
+    pSharedState->ReleaseCapture();
+  }
 }
 
 void CPWL_Wnd::SetFocus() {
-  if (CPWL_MsgControl* pMsgCtrl = GetMsgControl()) {
-    if (!pMsgCtrl->IsMainCaptureKeyboard(this))
-      pMsgCtrl->KillFocus();
-    pMsgCtrl->SetFocus(this);
+  if (SharedCaptureFocusState* pSharedState = GetSharedCaptureFocusState()) {
+    if (!pSharedState->IsMainCaptureKeyboard(this)) {
+      pSharedState->ReleaseFocus();
+    }
+    pSharedState->SetFocus(this);
   }
 }
 
 void CPWL_Wnd::KillFocus() {
-  if (CPWL_MsgControl* pMsgCtrl = GetMsgControl()) {
-    if (pMsgCtrl->IsWndCaptureKeyboard(this))
-      pMsgCtrl->KillFocus();
+  if (SharedCaptureFocusState* pSharedState = GetSharedCaptureFocusState()) {
+    if (pSharedState->IsWndCaptureKeyboard(this)) {
+      pSharedState->ReleaseFocus();
+    }
   }
 }
 
@@ -555,17 +576,21 @@
   if (!IsValid())
     return true;
 
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   for (const auto& pChild : m_Children) {
-    pChild->SetVisible(bVisible);
-    if (!thisObserved)
+    if (!pChild->SetVisible(bVisible)) {
       return false;
+    }
+    if (!this_observed) {
+      return false;
+    }
   }
 
   if (bVisible != m_bVisible) {
     m_bVisible = bVisible;
-    if (!RePosChildWnd())
+    if (!RepositionChildWnd()) {
       return false;
+    }
 
     if (!InvalidateRect(nullptr))
       return false;
@@ -586,7 +611,7 @@
   return HasFlag(PWS_READONLY);
 }
 
-bool CPWL_Wnd::RePosChildWnd() {
+bool CPWL_Wnd::RepositionChildWnd() {
   CPWL_ScrollBar* pVSB = GetVScrollBar();
   if (!pVSB)
     return true;
@@ -601,10 +626,11 @@
       CFX_FloatRect(rcContent.right - CPWL_ScrollBar::kWidth, rcContent.bottom,
                     rcContent.right - 1.0f, rcContent.top);
 
-  ObservedPtr<CPWL_Wnd> thisObserved(this);
+  ObservedPtr<CPWL_Wnd> this_observed(this);
   pVSB->Move(rcVScroll, true, false);
-  if (!thisObserved)
+  if (!this_observed) {
     return false;
+  }
 
   return true;
 }
@@ -616,19 +642,29 @@
     GetFillerNotify()->SetCursor(GetCreationParams()->eCursorType);
 }
 
-void CPWL_Wnd::CreateMsgControl() {
-  if (!m_CreationParams.pMsgControl)
-    m_CreationParams.pMsgControl = new CPWL_MsgControl(this);
+void CPWL_Wnd::CreateSharedCaptureFocusState() {
+  if (!m_CreationParams.pSharedCaptureFocusState) {
+    m_CreationParams.pSharedCaptureFocusState =
+        new SharedCaptureFocusState(this);
+  }
 }
 
-void CPWL_Wnd::DestroyMsgControl() {
-  CPWL_MsgControl* pMsgControl = GetMsgControl();
-  if (pMsgControl && pMsgControl->IsWndCreated(this))
-    delete pMsgControl;
+void CPWL_Wnd::DestroySharedCaptureFocusState() {
+  SharedCaptureFocusState* pSharedCaptureFocusState =
+      GetSharedCaptureFocusState();
+  if (!pSharedCaptureFocusState) {
+    return;
+  }
+  const bool owned = pSharedCaptureFocusState->IsOwnedByWnd(this);
+  pSharedCaptureFocusState->RemoveWnd(this);
+  if (owned) {
+    delete pSharedCaptureFocusState;
+  }
 }
 
-CPWL_MsgControl* CPWL_Wnd::GetMsgControl() const {
-  return m_CreationParams.pMsgControl;
+CPWL_Wnd::SharedCaptureFocusState* CPWL_Wnd::GetSharedCaptureFocusState()
+    const {
+  return m_CreationParams.pSharedCaptureFocusState;
 }
 
 bool CPWL_Wnd::IsCaptureMouse() const {
@@ -636,17 +672,17 @@
 }
 
 bool CPWL_Wnd::IsWndCaptureMouse(const CPWL_Wnd* pWnd) const {
-  CPWL_MsgControl* pCtrl = GetMsgControl();
+  SharedCaptureFocusState* pCtrl = GetSharedCaptureFocusState();
   return pCtrl && pCtrl->IsWndCaptureMouse(pWnd);
 }
 
 bool CPWL_Wnd::IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const {
-  CPWL_MsgControl* pCtrl = GetMsgControl();
+  SharedCaptureFocusState* pCtrl = GetSharedCaptureFocusState();
   return pCtrl && pCtrl->IsWndCaptureKeyboard(pWnd);
 }
 
 bool CPWL_Wnd::IsFocused() const {
-  CPWL_MsgControl* pCtrl = GetMsgControl();
+  SharedCaptureFocusState* pCtrl = GetSharedCaptureFocusState();
   return pCtrl && pCtrl->IsMainCaptureKeyboard(this);
 }
 
diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h
index b10b59a..f7aea8d 100644
--- a/fpdfsdk/pwl/cpwl_wnd.h
+++ b/fpdfsdk/pwl/cpwl_wnd.h
@@ -14,6 +14,7 @@
 #include "core/fxcrt/mask.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxcrt/widestring.h"
 #include "core/fxge/cfx_color.h"
 #include "core/fxge/cfx_renderdevice.h"
@@ -21,7 +22,6 @@
 #include "public/fpdf_fwlevent.h"
 
 class CPWL_Edit;
-class CPWL_MsgControl;
 class CPWL_ScrollBar;
 class IPVT_FontMap;
 struct PWL_SCROLL_INFO;
@@ -79,6 +79,8 @@
   static const CFX_Color kDefaultBlackColor;
   static const CFX_Color kDefaultWhiteColor;
 
+  class SharedCaptureFocusState;
+
   class ProviderIface : public Observable {
    public:
     virtual ~ProviderIface() = default;
@@ -118,7 +120,9 @@
     CPWL_Dash sDash;
 
     // Ignore, used internally only:
-    CPWL_MsgControl* pMsgControl = nullptr;
+    // TODO(tsepez): fix murky ownership, bare delete.
+    UNOWNED_PTR_EXCLUSION SharedCaptureFocusState* pSharedCaptureFocusState =
+        nullptr;
     IPWL_FillerNotify::CursorStyle eCursorType =
         IPWL_FillerNotify::CursorStyle::kArrow;
   };
@@ -136,7 +140,7 @@
   virtual ~CPWL_Wnd();
 
   // Returns |true| iff this instance is still allocated.
-  virtual bool InvalidateRect(const CFX_FloatRect* pRect);
+  [[nodiscard]] virtual bool InvalidateRect(const CFX_FloatRect* pRect);
 
   virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag);
   virtual bool OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag);
@@ -163,7 +167,7 @@
   virtual void SetCursor();
 
   // Returns |true| iff this instance is still allocated.
-  virtual bool SetVisible(bool bVisible);
+  [[nodiscard]] virtual bool SetVisible(bool bVisible);
   virtual void SetFontSize(float fFontSize);
   virtual float GetFontSize() const;
 
@@ -222,7 +226,7 @@
   virtual void CreateChildWnd(const CreateParams& cp);
 
   // Returns |true| iff this instance is still allocated.
-  virtual bool RePosChildWnd();
+  [[nodiscard]] virtual bool RepositionChildWnd();
 
   virtual void DrawThisAppearance(CFX_RenderDevice* pDevice,
                                   const CFX_Matrix& mtUser2Device);
@@ -246,8 +250,8 @@
   CPWL_ScrollBar* GetVScrollBar() const;
 
   // Returns |true| iff this instance is still allocated.
-  bool InvalidateRectMove(const CFX_FloatRect& rcOld,
-                          const CFX_FloatRect& rcNew);
+  [[nodiscard]] bool InvalidateRectMove(const CFX_FloatRect& rcOld,
+                                        const CFX_FloatRect& rcNew);
 
   void SetCapture();
   void ReleaseCapture();
@@ -275,13 +279,12 @@
 
   CFX_FloatRect PWLtoWnd(const CFX_FloatRect& rect) const;
 
-  void CreateScrollBar(const CreateParams& cp);
   void CreateVScrollBar(const CreateParams& cp);
 
   void AdjustStyle();
-  void CreateMsgControl();
-  void DestroyMsgControl();
-  CPWL_MsgControl* GetMsgControl() const;
+  void CreateSharedCaptureFocusState();
+  void DestroySharedCaptureFocusState();
+  SharedCaptureFocusState* GetSharedCaptureFocusState() const;
 
   CreateParams m_CreationParams;
   std::unique_ptr<IPWL_FillerNotify::PerWindowData> m_pAttachedData;
diff --git a/fxbarcode/BC_TwoDimWriter.h b/fxbarcode/BC_TwoDimWriter.h
index 3f9e4a7..cab1f45 100644
--- a/fxbarcode/BC_TwoDimWriter.h
+++ b/fxbarcode/BC_TwoDimWriter.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "fxbarcode/BC_Writer.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_CommonBitMatrix;
 class CFX_RenderDevice;
diff --git a/fxbarcode/cfx_barcode.cpp b/fxbarcode/cfx_barcode.cpp
index f7cd5f6..5b2c134 100644
--- a/fxbarcode/cfx_barcode.cpp
+++ b/fxbarcode/cfx_barcode.cpp
@@ -18,8 +18,8 @@
 #include "fxbarcode/cbc_pdf417i.h"
 #include "fxbarcode/cbc_qrcode.h"
 #include "fxbarcode/cbc_upca.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -50,8 +50,7 @@
     case BC_TYPE::kUnknown:
       return nullptr;
   }
-  NOTREACHED();
-  return nullptr;
+  NOTREACHED_NORETURN();
 }
 
 }  // namespace
diff --git a/fxbarcode/common/BC_CommonByteMatrix.h b/fxbarcode/common/BC_CommonByteMatrix.h
index 6ce6119..fa97f69 100644
--- a/fxbarcode/common/BC_CommonByteMatrix.h
+++ b/fxbarcode/common/BC_CommonByteMatrix.h
@@ -10,7 +10,7 @@
 #include <stdint.h>
 
 #include "core/fxcrt/data_vector.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_CommonByteMatrix final {
  public:
diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.cpp b/fxbarcode/datamatrix/BC_SymbolInfo.cpp
index 05efd56..59d40d0 100644
--- a/fxbarcode/datamatrix/BC_SymbolInfo.cpp
+++ b/fxbarcode/datamatrix/BC_SymbolInfo.cpp
@@ -106,8 +106,7 @@
     case 36:
       return 6;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -124,8 +123,7 @@
     case 36:
       return 6;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
diff --git a/fxbarcode/oned/BC_OneDimWriter.h b/fxbarcode/oned/BC_OneDimWriter.h
index 49957b5..3abcce4 100644
--- a/fxbarcode/oned/BC_OneDimWriter.h
+++ b/fxbarcode/oned/BC_OneDimWriter.h
@@ -18,7 +18,7 @@
 #include "core/fxge/cfx_textrenderoptions.h"
 #include "fxbarcode/BC_Library.h"
 #include "fxbarcode/BC_Writer.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_Font;
 class CFX_Matrix;
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
index 160275e..fc40701 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h
@@ -10,7 +10,7 @@
 #include <stdint.h>
 
 #include "core/fxcrt/fixed_zeroed_data_vector.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_BarcodeRow final {
  public:
diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
index 16b80da..6e8f034 100644
--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
+++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h
@@ -10,7 +10,7 @@
 #include "core/fxcrt/widestring.h"
 #include "fxbarcode/pdf417/BC_PDF417.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_PDF417HighLevelEncoder {
  public:
diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.h b/fxbarcode/qrcode/BC_QRCoderBitVector.h
index 1bfa24b..51bbe7c 100644
--- a/fxbarcode/qrcode/BC_QRCoderBitVector.h
+++ b/fxbarcode/qrcode/BC_QRCoderBitVector.h
@@ -11,7 +11,7 @@
 #include <stdint.h>
 
 #include "core/fxcrt/data_vector.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CBC_QRCoderBitVector {
  public:
diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
index f518c8b..10fac61 100644
--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
+++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp
@@ -196,8 +196,7 @@
       intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
       break;
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
   return intermediate == 0;
 }
diff --git a/fxjs/cfx_globaldata.cpp b/fxjs/cfx_globaldata.cpp
index 59b43c1..6d6efd5 100644
--- a/fxjs/cfx_globaldata.cpp
+++ b/fxjs/cfx_globaldata.cpp
@@ -75,7 +75,6 @@
     }
     // Arrays don't get persisted per JS spec page 484.
     case CFX_Value::DataType::kObject:
-    default:
       break;
   }
   return false;
@@ -293,10 +292,12 @@
     return false;
 
   for (int32_t i = 0, sz = dwCount; i < sz; i++) {
-    if (p > buffer.end())
+    if (p + sizeof(uint32_t) >= buffer.end()) {
       break;
+    }
 
-    uint32_t dwNameLen = *((uint32_t*)p);
+    uint32_t dwNameLen = 0;
+    memcpy(&dwNameLen, p, sizeof(uint32_t));
     p += sizeof(uint32_t);
     if (p + dwNameLen > buffer.end())
       break;
@@ -304,21 +305,25 @@
     ByteString sEntry = ByteString(p, dwNameLen);
     p += sizeof(char) * dwNameLen;
 
-    CFX_Value::DataType wDataType =
-        static_cast<CFX_Value::DataType>(*((uint16_t*)p));
+    uint16_t wDataType = 0;
+    memcpy(&wDataType, p, sizeof(uint16_t));
     p += sizeof(uint16_t);
 
-    switch (wDataType) {
+    CFX_Value::DataType eDataType = static_cast<CFX_Value::DataType>(wDataType);
+
+    switch (eDataType) {
       case CFX_Value::DataType::kNumber: {
         double dData = 0;
         switch (wVersion) {
           case 1: {
-            uint32_t dwData = *((uint32_t*)p);
+            uint32_t dwData = 0;
+            memcpy(&dwData, p, sizeof(uint32_t));
             p += sizeof(uint32_t);
             dData = dwData;
           } break;
           case 2: {
-            dData = *((double*)p);
+            dData = 0;
+            memcpy(&dData, p, sizeof(double));
             p += sizeof(double);
           } break;
         }
@@ -326,13 +331,15 @@
         SetGlobalVariablePersistent(sEntry, true);
       } break;
       case CFX_Value::DataType::kBoolean: {
-        uint16_t wData = *((uint16_t*)p);
+        uint16_t wData = 0;
+        memcpy(&wData, p, sizeof(uint16_t));
         p += sizeof(uint16_t);
         SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
         SetGlobalVariablePersistent(sEntry, true);
       } break;
       case CFX_Value::DataType::kString: {
-        uint32_t dwLength = *((uint32_t*)p);
+        uint32_t dwLength = 0;
+        memcpy(&dwLength, p, sizeof(uint32_t));
         p += sizeof(uint32_t);
         if (p + dwLength > buffer.end())
           break;
@@ -346,8 +353,7 @@
         SetGlobalVariablePersistent(sEntry, true);
       } break;
       case CFX_Value::DataType::kObject:
-      default:
-        // Arrays aren't allowed in these buffers, nor are unrecoginzed tags.
+        // Arrays aren't allowed in these buffers, nor are unrecognized tags.
         return false;
     }
   }
diff --git a/fxjs/cfx_globaldata.h b/fxjs/cfx_globaldata.h
index 6b94265..ac94058 100644
--- a/fxjs/cfx_globaldata.h
+++ b/fxjs/cfx_globaldata.h
@@ -14,7 +14,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cfx_keyvalue.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFX_GlobalData {
  public:
diff --git a/fxjs/cjs_field.cpp b/fxjs/cjs_field.cpp
index 53b3449..c9127dd 100644
--- a/fxjs/cjs_field.cpp
+++ b/fxjs/cjs_field.cpp
@@ -235,7 +235,7 @@
                     const ByteString& bsString) {
   DCHECK(pFormFillEnv);
 
-  BorderStyle nBorderStyle = BorderStyle::kSolid;
+  BorderStyle nBorderStyle;
   if (bsString == "solid")
     nBorderStyle = BorderStyle::kSolid;
   else if (bsString == "beveled")
@@ -2022,8 +2022,10 @@
       return CJS_Result::Success(pRuntime->NewString("text"));
     case FormFieldType::kSignature:
       return CJS_Result::Success(pRuntime->NewString("signature"));
+#ifdef PDF_ENABLE_XFA
     default:
       return CJS_Result::Success(pRuntime->NewString("unknown"));
+#endif
   }
 }
 
@@ -2633,7 +2635,5 @@
       SetFieldValue(pFormFillEnv, pData->sFieldName, pData->nControlIndex,
                     pData->widestringarray);
       break;
-    default:
-      NOTREACHED();
   }
 }
diff --git a/fxjs/cjs_global.cpp b/fxjs/cjs_global.cpp
index 101dee2..5582682 100644
--- a/fxjs/cjs_global.cpp
+++ b/fxjs/cjs_global.cpp
@@ -201,10 +201,7 @@
           v8::Local<v8::Object>::New(pRuntime->GetIsolate(), pData->pData));
     case CFX_Value::DataType::kNull:
       return CJS_Result::Success(pRuntime->NewNull());
-    default:
-      break;
   }
-  return CJS_Result::Failure(JSMessage::kObjectTypeError);
 }
 
 CJS_Result CJS_Global::SetProperty(CJS_Runtime* pRuntime,
@@ -491,8 +488,6 @@
         break;
       case CFX_Value::DataType::kNull:
         break;
-      default:
-        return CJS_Result::Failure(JSMessage::kObjectTypeError);
     }
     return CJS_Result::Success();
   }
@@ -523,8 +518,6 @@
       pNewData->nType = CFX_Value::DataType::kNull;
       pNewData->bPersistent = bDefaultPersistent;
       break;
-    default:
-      return CJS_Result::Failure(JSMessage::kObjectTypeError);
   }
   m_MapGlobal[propname] = std::move(pNewData);
   return CJS_Result::Success();
diff --git a/fxjs/cjs_object.h b/fxjs/cjs_object.h
index c53568e..4c567f2 100644
--- a/fxjs/cjs_object.h
+++ b/fxjs/cjs_object.h
@@ -9,7 +9,7 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/cjs_runtime.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class CFXJS_Engine;
 
diff --git a/fxjs/fxv8.h b/fxjs/fxv8.h
index 5ebd7f5..62c9bb3 100644
--- a/fxjs/fxv8.h
+++ b/fxjs/fxv8.h
@@ -12,7 +12,7 @@
 #include <vector>
 
 #include "core/fxcrt/fx_string.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "v8/include/v8-forward.h"
 
 // The fxv8 functions soften up the interface to the V8 API. In particular,
diff --git a/fxjs/js_resources.cpp b/fxjs/js_resources.cpp
index c133610..5c054fd 100644
--- a/fxjs/js_resources.cpp
+++ b/fxjs/js_resources.cpp
@@ -6,8 +6,6 @@
 
 #include "fxjs/js_resources.h"
 
-#include "third_party/base/notreached.h"
-
 WideString JSGetStringFromID(JSMessage msg) {
   const char* msg_string = "";
   switch (msg) {
@@ -92,9 +90,6 @@
     case JSMessage::kWouldBeCyclic:
       msg_string = "Operation would create a cycle.";
       break;
-    default:
-      NOTREACHED();
-      break;
   }
   return WideString::FromASCII(msg_string);
 }
diff --git a/fxjs/xfa/cfxjse_class.cpp b/fxjs/xfa/cfxjse_class.cpp
index 26b4666..c600bd7 100644
--- a/fxjs/xfa/cfxjse_class.cpp
+++ b/fxjs/xfa/cfxjse_class.cpp
@@ -103,7 +103,7 @@
   }
 
   v8::Local<v8::String> hPropName =
-      hCallBackInfo->GetInternalField(1).As<v8::String>();
+      hCallBackInfo->GetInternalField(1).As<v8::Value>().As<v8::String>();
   if (hPropName.IsEmpty())
     return;
 
diff --git a/fxjs/xfa/cfxjse_context.cpp b/fxjs/xfa/cfxjse_context.cpp
index 970cb3d..337b443 100644
--- a/fxjs/xfa/cfxjse_context.cpp
+++ b/fxjs/xfa/cfxjse_context.cpp
@@ -17,7 +17,7 @@
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/memory/ptr_util.h"
 #include "v8/include/v8-exception.h"
 #include "v8/include/v8-function.h"
 #include "v8/include/v8-message.h"
diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp
index 601ffb4..ecb8c96 100644
--- a/fxjs/xfa/cfxjse_engine.cpp
+++ b/fxjs/xfa/cfxjse_engine.cpp
@@ -547,7 +547,8 @@
   for (int i = 0; i < info.Length(); i++)
     parameters.push_back(info[i]);
 
-  return pObject->JSObject()->RunMethod(functionName, parameters);
+  return pObject->JSObject()->RunMethod(pScriptContext, functionName,
+                                        parameters);
 }
 
 bool CFXJSE_Engine::IsStrictScopeInJavaScript() {
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index 4fc19d7..a99c135 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "core/fxcrt/cfx_datetime.h"
+#include "core/fxcrt/code_point_view.h"
 #include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_random.h"
@@ -31,7 +32,6 @@
 #include "fxjs/xfa/cjx_object.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "v8/include/v8-container.h"
 #include "v8/include/v8-function-callback.h"
@@ -510,316 +510,6 @@
   return bsGUID;
 }
 
-bool IsIsoDateFormat(pdfium::span<const char> pData,
-                     int32_t* pStyle,
-                     int32_t* pYear,
-                     int32_t* pMonth,
-                     int32_t* pDay) {
-  int32_t& iStyle = *pStyle;
-  int32_t& iYear = *pYear;
-  int32_t& iMonth = *pMonth;
-  int32_t& iDay = *pDay;
-
-  iYear = 0;
-  iMonth = 1;
-  iDay = 1;
-
-  if (pData.size() < 4)
-    return false;
-
-  char szYear[5];
-  szYear[4] = '\0';
-  for (int32_t i = 0; i < 4; ++i) {
-    if (!isdigit(pData[i]))
-      return false;
-
-    szYear[i] = pData[i];
-  }
-  iYear = FXSYS_atoi(szYear);
-  iStyle = 0;
-  if (pData.size() == 4)
-    return true;
-
-  iStyle = pData[4] == '-' ? 1 : 0;
-
-  size_t iPosOff = iStyle == 0 ? 4 : 5;
-  if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1]))
-    return false;
-
-  char szBuffer[3] = {};
-  szBuffer[0] = pData[iPosOff];
-  szBuffer[1] = pData[iPosOff + 1];
-  iMonth = FXSYS_atoi(szBuffer);
-  if (iMonth > 12 || iMonth < 1)
-    return false;
-
-  if (iStyle == 0) {
-    iPosOff += 2;
-    if (pData.size() == 6)
-      return true;
-  } else {
-    iPosOff += 3;
-    if (pData.size() == 7)
-      return true;
-  }
-  if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1]))
-    return false;
-
-  szBuffer[0] = pData[iPosOff];
-  szBuffer[1] = pData[iPosOff + 1];
-  iDay = FXSYS_atoi(szBuffer);
-  if (iPosOff + 2 < pData.size())
-    return false;
-
-  if (iMonth == 2) {
-    bool bIsLeap = (!(iYear % 4) && (iYear % 100)) || !(iYear % 400);
-    return iDay <= (bIsLeap ? 29 : 28);
-  }
-
-  if (iMonth < 8)
-    return iDay <= (iMonth % 2 == 0 ? 30 : 31);
-  return iDay <= (iMonth % 2 == 0 ? 31 : 30);
-}
-
-bool IsIsoTimeFormat(pdfium::span<const char> pData,
-                     int32_t* pHour,
-                     int32_t* pMinute,
-                     int32_t* pSecond,
-                     int32_t* pMilliSecond,
-                     int32_t* pZoneHour,
-                     int32_t* pZoneMinute) {
-  int32_t& iHour = *pHour;
-  int32_t& iMinute = *pMinute;
-  int32_t& iSecond = *pSecond;
-  int32_t& iMilliSecond = *pMilliSecond;
-  int32_t& iZoneHour = *pZoneHour;
-  int32_t& iZoneMinute = *pZoneMinute;
-
-  iHour = 0;
-  iMinute = 0;
-  iSecond = 0;
-  iMilliSecond = 0;
-  iZoneHour = 0;
-  iZoneMinute = 0;
-
-  if (pData.empty())
-    return false;
-
-  size_t iZone = 0;
-  size_t i = 0;
-  while (i < pData.size()) {
-    if (!isdigit(pData[i]) && pData[i] != ':') {
-      iZone = i;
-      break;
-    }
-    ++i;
-  }
-  if (i == pData.size())
-    iZone = pData.size();
-
-  char szBuffer[3] = {};
-  size_t iPos = 0;
-  size_t iIndex = 0;
-  while (iIndex < iZone) {
-    if (!isdigit(pData[iIndex]))
-      return false;
-
-    szBuffer[0] = pData[iIndex];
-    if (!isdigit(pData[iIndex + 1]))
-      return false;
-
-    szBuffer[1] = pData[iIndex + 1];
-    if (FXSYS_atoi(szBuffer) > 60)
-      return false;
-
-    if (pData[2] == ':') {
-      if (iPos == 0) {
-        iHour = FXSYS_atoi(szBuffer);
-        ++iPos;
-      } else if (iPos == 1) {
-        iMinute = FXSYS_atoi(szBuffer);
-        ++iPos;
-      } else {
-        iSecond = FXSYS_atoi(szBuffer);
-      }
-      iIndex += 3;
-    } else {
-      if (iPos == 0) {
-        iHour = FXSYS_atoi(szBuffer);
-        ++iPos;
-      } else if (iPos == 1) {
-        iMinute = FXSYS_atoi(szBuffer);
-        ++iPos;
-      } else if (iPos == 2) {
-        iSecond = FXSYS_atoi(szBuffer);
-        ++iPos;
-      }
-      iIndex += 2;
-    }
-  }
-
-  if (iIndex < pData.size() && pData[iIndex] == '.') {
-    constexpr int kSubSecondLength = 3;
-    if (iIndex + kSubSecondLength >= pData.size())
-      return false;
-
-    ++iIndex;
-    char szMilliSeconds[kSubSecondLength + 1];
-    for (int j = 0; j < kSubSecondLength; ++j) {
-      char c = pData[iIndex + j];
-      if (!isdigit(c))
-        return false;
-      szMilliSeconds[j] = c;
-    }
-    szMilliSeconds[kSubSecondLength] = '\0';
-
-    iMilliSecond = FXSYS_atoi(szMilliSeconds);
-    if (iMilliSecond > 100) {
-      iMilliSecond = 0;
-      return false;
-    }
-    iIndex += kSubSecondLength;
-  }
-
-  if (iIndex < pData.size() && FXSYS_towlower(pData[iIndex]) == 'z')
-    return true;
-
-  int32_t iSign = 1;
-  if (iIndex < pData.size()) {
-    if (pData[iIndex] == '+') {
-      ++iIndex;
-    } else if (pData[iIndex] == '-') {
-      iSign = -1;
-      ++iIndex;
-    }
-  }
-  iPos = 0;
-  while (iIndex < pData.size()) {
-    if (!isdigit(pData[iIndex]))
-      return false;
-
-    szBuffer[0] = pData[iIndex];
-    if (!isdigit(pData[iIndex + 1]))
-      return false;
-
-    szBuffer[1] = pData[iIndex + 1];
-    if (FXSYS_atoi(szBuffer) > 60)
-      return false;
-
-    if (pData[2] == ':') {
-      if (iPos == 0) {
-        iZoneHour = FXSYS_atoi(szBuffer);
-      } else if (iPos == 1) {
-        iZoneMinute = FXSYS_atoi(szBuffer);
-      }
-      iIndex += 3;
-    } else {
-      if (!iPos) {
-        iZoneHour = FXSYS_atoi(szBuffer);
-        ++iPos;
-      } else if (iPos == 1) {
-        iZoneMinute = FXSYS_atoi(szBuffer);
-        ++iPos;
-      }
-      iIndex += 2;
-    }
-  }
-  if (iIndex < pData.size())
-    return false;
-
-  iZoneHour *= iSign;
-  return true;
-}
-
-bool IsIsoDateTimeFormat(pdfium::span<const char> pData,
-                         int32_t* pYear,
-                         int32_t* pMonth,
-                         int32_t* pDay,
-                         int32_t* pHour,
-                         int32_t* pMinute,
-                         int32_t* pSecond,
-                         int32_t* pMilliSecond,
-                         int32_t* pZoneHour,
-                         int32_t* pZoneMinute) {
-  *pYear = 0;
-  *pMonth = 0;
-  *pDay = 0;
-  *pHour = 0;
-  *pMinute = 0;
-  *pSecond = 0;
-
-  size_t iIndex = 0;
-  while (iIndex < pData.size()) {
-    if (pData[iIndex] == 'T' || pData[iIndex] == 't')
-      break;
-    ++iIndex;
-  }
-  if (iIndex == pData.size() || (iIndex != 8 && iIndex != 10))
-    return false;
-
-  pdfium::span<const char> pDateSpan = pData.subspan(0, iIndex);
-  pdfium::span<const char> pTimeSpan = pData.subspan(iIndex + 1);
-
-  int32_t iStyle = -1;
-  return IsIsoDateFormat(pDateSpan, &iStyle, pYear, pMonth, pDay) &&
-         IsIsoTimeFormat(pTimeSpan, pHour, pMinute, pSecond, pMilliSecond,
-                         pZoneHour, pZoneMinute);
-}
-
-int32_t DateString2Num(ByteStringView bsDate) {
-  int32_t iLength = bsDate.GetLength();
-  int32_t iYear = 0;
-  int32_t iMonth = 0;
-  int32_t iDay = 0;
-  if (iLength <= 10) {
-    int32_t iStyle = -1;
-    if (!IsIsoDateFormat(bsDate.span(), &iStyle, &iYear, &iMonth, &iDay))
-      return 0;
-  } else {
-    int32_t iHour = 0;
-    int32_t iMinute = 0;
-    int32_t iSecond = 0;
-    int32_t iMilliSecond = 0;
-    int32_t iZoneHour = 0;
-    int32_t iZoneMinute = 0;
-    if (!IsIsoDateTimeFormat(bsDate.span(), &iYear, &iMonth, &iDay, &iHour,
-                             &iMinute, &iSecond, &iMilliSecond, &iZoneHour,
-                             &iZoneMinute)) {
-      return 0;
-    }
-  }
-
-  float dDays = 0;
-  int32_t i = 1;
-  if (iYear < 1900)
-    return 0;
-
-  while (iYear - i >= 1900) {
-    dDays +=
-        ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400))
-            ? 366
-            : 365;
-    ++i;
-  }
-  i = 1;
-  while (i < iMonth) {
-    if (i == 2)
-      dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28;
-    else if (i <= 7)
-      dDays += (i % 2 == 0) ? 30 : 31;
-    else
-      dDays += (i % 2 == 0) ? 31 : 30;
-
-    ++i;
-  }
-  i = 0;
-  while (iDay - i > 0) {
-    ++dDays;
-    ++i;
-  }
-  return static_cast<int32_t>(dDays);
-}
-
 void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) {
   time_t now;
   FXSYS_time(&now);
@@ -967,18 +657,20 @@
 }
 
 WideString EncodeURL(const ByteString& bsURL) {
-  static const wchar_t kStrUnsafe[] = {' ', '<',  '>', '"', '#', '%', '{', '}',
-                                       '|', '\\', '^', '~', '[', ']', '`'};
-  static const wchar_t kStrReserved[] = {';', '/', '?', ':', '@', '=', '&'};
-  static const wchar_t kStrSpecial[] = {'$',  '-', '+', '!', '*',
-                                        '\'', '(', ')', ','};
+  static constexpr char32_t kStrUnsafe[] = {' ', '<', '>', '"', '#',
+                                            '%', '{', '}', '|', '\\',
+                                            '^', '~', '[', ']', '`'};
+  static constexpr char32_t kStrReserved[] = {';', '/', '?', ':',
+                                              '@', '=', '&'};
+  static constexpr char32_t kStrSpecial[] = {'$',  '-', '+', '!', '*',
+                                             '\'', '(', ')', ','};
 
   WideString wsURL = WideString::FromUTF8(bsURL.AsStringView());
   WideTextBuffer wsResultBuf;
   wchar_t szEncode[4];
   szEncode[0] = '%';
   szEncode[3] = 0;
-  for (wchar_t ch : wsURL) {
+  for (char32_t ch : pdfium::CodePointView(wsURL.AsStringView())) {
     size_t i = 0;
     size_t iCount = std::size(kStrUnsafe);
     while (i < iCount) {
@@ -1070,7 +762,7 @@
   szEncode[1] = '#';
   szEncode[2] = 'x';
   WideTextBuffer wsResultBuf;
-  for (uint32_t ch : wsHTML) {
+  for (char32_t ch : pdfium::CodePointView(wsHTML.AsStringView())) {
     WideString htmlReserve;
     if (HTMLCode2STR(ch, &htmlReserve)) {
       wsResultBuf.AppendChar(L'&');
@@ -1109,7 +801,7 @@
   szEncode[0] = '&';
   szEncode[1] = '#';
   szEncode[2] = 'x';
-  for (uint32_t ch : wsXML) {
+  for (char32_t ch : pdfium::CodePointView(wsXML.AsStringView())) {
     switch (ch) {
       case '"':
         wsResultBuf.AppendChar('&');
@@ -1885,7 +1577,7 @@
       return;
     }
     double dPrecision = maybe_precision.value();
-    uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
+    uPrecision = static_cast<uint8_t>(std::clamp(dPrecision, 0.0, 12.0));
   }
 
   CFGAS_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
@@ -4321,10 +4013,10 @@
   ByteString bsSource = ValueToUTF8String(info.GetIsolate(), sourceValue);
   int32_t iLength = pdfium::base::checked_cast<int32_t>(bsSource.GetLength());
   if (iLength) {
-    iStart = pdfium::clamp(
+    iStart = std::clamp(
         static_cast<int32_t>(ValueToFloat(info.GetIsolate(), startValue)), 1,
         iLength);
-    iDelete = pdfium::clamp(
+    iDelete = std::clamp(
         static_cast<int32_t>(ValueToFloat(info.GetIsolate(), deleteValue)), 0,
         iLength - iStart + 1);
   }
@@ -5440,6 +5132,279 @@
   info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values));
 }
 
+// static
+bool CFXJSE_FormCalcContext::IsIsoDateFormat(ByteStringView bsData,
+                                             int32_t* pYear,
+                                             int32_t* pMonth,
+                                             int32_t* pDay) {
+  pdfium::span<const char> pData = bsData.span();
+
+  int32_t& iYear = *pYear;
+  int32_t& iMonth = *pMonth;
+  int32_t& iDay = *pDay;
+
+  iYear = 0;
+  iMonth = 1;
+  iDay = 1;
+
+  if (pData.size() < 4) {
+    return false;
+  }
+
+  char szYear[5];
+  szYear[4] = '\0';
+  for (int32_t i = 0; i < 4; ++i) {
+    if (!isdigit(pData[i])) {
+      return false;
+    }
+
+    szYear[i] = pData[i];
+  }
+  iYear = FXSYS_atoi(szYear);
+  if (pData.size() == 4) {
+    return true;
+  }
+
+  int32_t iStyle = pData[4] == '-' ? 1 : 0;
+  size_t iPosOff = iStyle == 0 ? 4 : 5;
+  if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1])) {
+    return false;
+  }
+
+  char szBuffer[3] = {};
+  szBuffer[0] = pData[iPosOff];
+  szBuffer[1] = pData[iPosOff + 1];
+  iMonth = FXSYS_atoi(szBuffer);
+  if (iMonth > 12 || iMonth < 1) {
+    return false;
+  }
+
+  if (iStyle == 0) {
+    iPosOff += 2;
+    if (pData.size() == 6) {
+      return true;
+    }
+  } else {
+    iPosOff += 3;
+    if (pData.size() == 7) {
+      return true;
+    }
+  }
+  if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1])) {
+    return false;
+  }
+
+  szBuffer[0] = pData[iPosOff];
+  szBuffer[1] = pData[iPosOff + 1];
+  iDay = FXSYS_atoi(szBuffer);
+  if (iPosOff + 2 < pData.size()) {
+    return false;
+  }
+
+  if (iMonth == 2) {
+    bool bIsLeap = (!(iYear % 4) && (iYear % 100)) || !(iYear % 400);
+    return iDay <= (bIsLeap ? 29 : 28);
+  }
+
+  if (iMonth < 8) {
+    return iDay <= (iMonth % 2 == 0 ? 30 : 31);
+  }
+  return iDay <= (iMonth % 2 == 0 ? 31 : 30);
+}
+
+// static
+bool CFXJSE_FormCalcContext::IsIsoTimeFormat(ByteStringView bsData) {
+  enum State { kHour, kMinute, kSecond, kZoneHour, kZoneMinute, kFinished };
+
+  pdfium::span<const char> pData = bsData.span();
+  if (pData.empty()) {
+    return false;
+  }
+
+  size_t iZone = 0;
+  size_t i = 0;
+  while (i < pData.size()) {
+    if (!isdigit(pData[i]) && pData[i] != ':') {
+      iZone = i;
+      break;
+    }
+    ++i;
+  }
+  if (i == pData.size()) {
+    iZone = pData.size();
+  }
+
+  char szBuffer[3] = {};  // Last char always stays NUL for termination.
+  State state = kHour;
+  size_t iIndex = 0;
+  while (iIndex + 1 < iZone) {
+    szBuffer[0] = pData[iIndex];
+    szBuffer[1] = pData[iIndex + 1];
+    if (!isdigit(szBuffer[0]) || !isdigit(szBuffer[1])) {
+      return false;
+    }
+    int32_t value = FXSYS_atoi(szBuffer);
+    if (state == kHour) {
+      if (value >= 24) {
+        return false;
+      }
+      state = kMinute;
+    } else if (state == kMinute) {
+      if (value >= 60) {
+        return false;
+      }
+      state = kSecond;
+    } else if (state == kSecond) {
+      // Allow leap second.
+      if (value > 60) {
+        return false;
+      }
+      state = kFinished;
+    } else {
+      return false;
+    }
+    iIndex += 2;
+    if (iIndex < iZone && pData[iIndex] == ':') {
+      ++iIndex;
+    }
+  }
+
+  if (iIndex < pData.size() && pData[iIndex] == '.') {
+    constexpr int kSubSecondLength = 3;
+    if (iIndex + kSubSecondLength >= pData.size()) {
+      return false;
+    }
+
+    ++iIndex;
+    char szMilliSeconds[kSubSecondLength + 1] = {};
+    for (int j = 0; j < kSubSecondLength; ++j) {
+      char c = pData[iIndex + j];
+      if (!isdigit(c)) {
+        return false;
+      }
+      szMilliSeconds[j] = c;
+    }
+    if (FXSYS_atoi(szMilliSeconds) >= 1000) {
+      return false;
+    }
+    iIndex += kSubSecondLength;
+  }
+
+  if (iIndex < pData.size() && FXSYS_towlower(pData[iIndex]) == 'z') {
+    return true;
+  }
+
+  if (iIndex < pData.size()) {
+    if (pData[iIndex] == '+') {
+      ++iIndex;
+    } else if (pData[iIndex] == '-') {
+      ++iIndex;
+    }
+  }
+  state = kZoneHour;
+  while (iIndex + 1 < pData.size()) {
+    szBuffer[0] = pData[iIndex];
+    szBuffer[1] = pData[iIndex + 1];
+    if (!isdigit(szBuffer[0]) || !isdigit(szBuffer[1])) {
+      return false;
+    }
+    int32_t value = FXSYS_atoi(szBuffer);
+    if (state == kZoneHour) {
+      if (value >= 24) {
+        return false;
+      }
+      state = kZoneMinute;
+    } else if (state == kZoneMinute) {
+      if (value >= 60) {
+        return false;
+      }
+      state = kFinished;
+    } else {
+      return false;
+    }
+    iIndex += 2;
+    if (iIndex < pData.size() && pData[iIndex] == ':') {
+      ++iIndex;
+    }
+  }
+
+  // Success if all input was processed.
+  return iIndex == pData.size();
+}
+
+bool CFXJSE_FormCalcContext::IsIsoDateTimeFormat(ByteStringView bsData,
+                                                 int32_t* pYear,
+                                                 int32_t* pMonth,
+                                                 int32_t* pDay) {
+  *pYear = 0;
+  *pMonth = 0;
+  *pDay = 0;
+
+  size_t iIndex = 0;
+  while (iIndex < bsData.GetLength()) {
+    if (bsData[iIndex] == 'T' || bsData[iIndex] == 't') {
+      break;
+    }
+    ++iIndex;
+  }
+  if (iIndex == bsData.GetLength() || (iIndex != 8 && iIndex != 10)) {
+    return false;
+  }
+
+  ByteStringView date_part = bsData.First(iIndex);
+  ByteStringView time_part = bsData.Substr(iIndex + 1);
+  return IsIsoDateFormat(date_part, pYear, pMonth, pDay) &&
+         IsIsoTimeFormat(time_part);
+}
+
+// static
+int32_t CFXJSE_FormCalcContext::DateString2Num(ByteStringView bsDate) {
+  int32_t iYear = 0;
+  int32_t iMonth = 0;
+  int32_t iDay = 0;
+  if (bsDate.GetLength() <= 10) {
+    if (!IsIsoDateFormat(bsDate, &iYear, &iMonth, &iDay)) {
+      return 0;
+    }
+  } else {
+    if (!IsIsoDateTimeFormat(bsDate, &iYear, &iMonth, &iDay)) {
+      return 0;
+    }
+  }
+
+  float dDays = 0;
+  int32_t i = 1;
+  if (iYear < 1900) {
+    return 0;
+  }
+
+  while (iYear - i >= 1900) {
+    dDays +=
+        ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400))
+            ? 366
+            : 365;
+    ++i;
+  }
+  i = 1;
+  while (i < iMonth) {
+    if (i == 2) {
+      dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28;
+    } else if (i <= 7) {
+      dDays += (i % 2 == 0) ? 30 : 31;
+    } else {
+      dDays += (i % 2 == 0) ? 31 : 30;
+    }
+
+    ++i;
+  }
+  i = 0;
+  while (iDay - i > 0) {
+    ++dDays;
+    ++i;
+  }
+  return static_cast<int32_t>(dDays);
+}
+
 bool CFXJSE_FormCalcContext::ApplyToExpansion(
     std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
     const v8::FunctionCallbackInfo<v8::Value>& info,
diff --git a/fxjs/xfa/cfxjse_formcalc_context.h b/fxjs/xfa/cfxjse_formcalc_context.h
index 3c7d271..a0bf84f 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.h
+++ b/fxjs/xfa/cfxjse_formcalc_context.h
@@ -11,6 +11,7 @@
 
 #include <functional>
 
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/widetext_buffer.h"
 #include "fxjs/xfa/fxjse.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -266,10 +267,6 @@
   static void concat_fm_object(CFXJSE_HostObject* pThis,
                                const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  static ByteString GenerateSomExpression(ByteStringView bsName,
-                                          int32_t iIndexFlags,
-                                          int32_t iIndexValue,
-                                          bool bIsStar);
   static absl::optional<WideTextBuffer> Translate(cppgc::Heap* pHeap,
                                                   WideStringView wsFormcalc);
 
@@ -278,10 +275,33 @@
   CXFA_Document* GetDocument() const { return m_pDocument.Get(); }
 
  private:
+  friend class FormCalcContextTest_GenerateSomExpression_Test;
+  friend class FormCalcContextTest_IsIsoDateFormat_Test;
+  friend class FormCalcContextTest_IsIsoTimeFormat_Test;
+
+  static ByteString GenerateSomExpression(ByteStringView bsName,
+                                          int32_t iIndexFlags,
+                                          int32_t iIndexValue,
+                                          bool bIsStar);
+
   static void DotAccessorCommon(CFXJSE_HostObject* pThis,
                                 const v8::FunctionCallbackInfo<v8::Value>& info,
                                 bool bDotAccessor);
 
+  static bool IsIsoDateTimeFormat(ByteStringView bsData,
+                                  int32_t* pYear,
+                                  int32_t* pMonth,
+                                  int32_t* pDay);
+
+  static bool IsIsoDateFormat(ByteStringView bsData,
+                              int32_t* pYear,
+                              int32_t* pMonth,
+                              int32_t* pDay);
+
+  static bool IsIsoTimeFormat(ByteStringView bsData);
+
+  static int32_t DateString2Num(ByteStringView bsDate);
+
   bool ApplyToExpansion(
       std::function<void(v8::Isolate*, v8::Local<v8::Value>)> fn,
       const v8::FunctionCallbackInfo<v8::Value>& info,
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
index 1478bf9..db03f03 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -11,7 +11,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/scoped_set_tz.h"
 #include "testing/xfa_js_embedder_test.h"
-#include "third_party/base/cxx17_backports.h"
 #include "xfa/fxfa/cxfa_eventparam.h"
 
 class CFXJSE_FormCalcContextEmbedderTest : public XFAJSEmbedderTest {
@@ -717,13 +716,9 @@
   ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")",
                       "&quot;&otilde;&#xfed0;");
 
-#if !BUILDFLAG(IS_WIN)
-  // Windows wchar_t isn't wide enough to handle these anyways.
-  // TODO(tsepez): fix surrogate encodings.
   ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9");
   ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"xml\")", "");
   ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"html\")", "");
-#endif  // !BUILDFLAG(IS_WIN)
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Format) {
diff --git a/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
index 2500e3f..ee9437e 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp
@@ -72,3 +72,72 @@
       "mars", 3, -2147483648, /*bIsStar=*/false);
   EXPECT_EQ(result, "mars[0]");
 }
+
+TEST(FormCalcContextTest, IsIsoDateFormat) {
+  int32_t year = 0;
+  int32_t month = 0;
+  int32_t day = 0;
+
+  EXPECT_FALSE(
+      CFXJSE_FormCalcContext::IsIsoDateFormat("", &year, &month, &day));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoDateFormat("2023-06-24", &year,
+                                                      &month, &day));
+  EXPECT_EQ(2023, year);
+  EXPECT_EQ(6, month);
+  EXPECT_EQ(24, day);
+}
+
+TEST(FormCalcContextTest, IsIsoTimeFormat) {
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat(""));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat(":"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("::"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat(":::"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("2"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("2:"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:3"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20304"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:4"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("2030405"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40:5"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("2030"));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040"));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040.001"));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40.001"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040z"));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40z"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040+07:30"));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40+07:30"));
+
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040-07:30"));
+  EXPECT_TRUE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40-07:30"));
+
+  // Range errors.
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("243040-07:30"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("24:30:40-07:30"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("206040-07:30"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:60:40-07:30"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203061-07:30"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:61-07:30"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040-24:30"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40-24:30"));
+
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("203040-07:60"));
+  EXPECT_FALSE(CFXJSE_FormCalcContext::IsIsoTimeFormat("20:30:40-07:60"));
+}
diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp
index 72309dc..66dbb07 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.cpp
+++ b/fxjs/xfa/cjx_eventpseudomodel.cpp
@@ -126,8 +126,8 @@
   if (bSetting)
     return;
 
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  CXFA_EventParam* pEventParam =
+      GetDocument()->GetScriptContext()->GetEventParam();
   if (!pEventParam)
     return;
 
@@ -201,8 +201,7 @@
 CJS_Result CJX_EventPseudoModel::emit(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  CXFA_EventParam* pEventParam = runtime->GetEventParam();
   if (!pEventParam)
     return CJS_Result::Success();
 
@@ -210,15 +209,14 @@
   if (!pNotify)
     return CJS_Result::Success();
 
-  pNotify->HandleWidgetEvent(pScriptContext->GetEventTarget(), pEventParam);
+  pNotify->HandleWidgetEvent(runtime->GetEventTarget(), pEventParam);
   return CJS_Result::Success();
 }
 
 CJS_Result CJX_EventPseudoModel::reset(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-  CXFA_EventParam* pEventParam = pScriptContext->GetEventParam();
+  CXFA_EventParam* pEventParam = runtime->GetEventParam();
   if (pEventParam)
     *pEventParam = CXFA_EventParam();
 
@@ -309,7 +307,6 @@
                      bSetting);
       break;
     case XFA_Event::Target:
-    default:
       break;
   }
 }
diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp
index 11e6982..641f0f2 100644
--- a/fxjs/xfa/cjx_exclgroup.cpp
+++ b/fxjs/xfa/cjx_exclgroup.cpp
@@ -109,9 +109,7 @@
   if (!pReturnNode)
     return CJS_Result::Success(runtime->NewNull());
 
-  return CJS_Result::Success(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pReturnNode));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pReturnNode));
 }
 
 void CJX_ExclGroup::defaultValue(v8::Isolate* pIsolate,
diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp
index 4dab966..ec68b70 100644
--- a/fxjs/xfa/cjx_form.cpp
+++ b/fxjs/xfa/cjx_form.cpp
@@ -82,8 +82,7 @@
 CJS_Result CJX_Form::recalculate(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  CXFA_EventParam* pEventParam =
-      GetDocument()->GetScriptContext()->GetEventParam();
+  CXFA_EventParam* pEventParam = runtime->GetEventParam();
   if (pEventParam && (pEventParam->m_eType == XFA_EVENT_Calculate ||
                       pEventParam->m_eType == XFA_EVENT_InitCalculate)) {
     return CJS_Result::Success();
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
index 597db30..f79a0cd 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -259,8 +259,9 @@
 CJS_Result CJX_HostPseudoModel::gotoURL(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+  if (!runtime->IsRunAtClient()) {
     return CJS_Result::Success();
+  }
 
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -276,8 +277,9 @@
 CJS_Result CJX_HostPseudoModel::openList(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+  if (!runtime->IsRunAtClient()) {
     return CJS_Result::Success();
+  }
 
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -290,8 +292,7 @@
   if (params[0]->IsObject()) {
     pNode = ToNode(runtime->ToXFAObject(params[0]));
   } else if (params[0]->IsString()) {
-    CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-    CXFA_Object* pObject = pScriptContext->GetThisObject();
+    CXFA_Object* pObject = runtime->GetThisObject();
     if (!pObject)
       return CJS_Result::Success();
 
@@ -299,7 +300,7 @@
                                               XFA_ResolveFlag::kParent,
                                               XFA_ResolveFlag::kSiblings};
     absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
-        pScriptContext->ResolveObjects(
+        runtime->ResolveObjects(
             pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags);
     if (!maybeResult.has_value() ||
         !maybeResult.value().objects.front()->IsNode()) {
@@ -376,8 +377,7 @@
   const size_t nExpLength = expression.GetLength();
   while (nStart < nExpLength) {
     nStart = FilterName(expression.AsStringView(), nStart, wsName);
-    CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-    CXFA_Object* pObject = pScriptContext->GetThisObject();
+    CXFA_Object* pObject = runtime->GetThisObject();
     if (!pObject)
       return CJS_Result::Success();
 
@@ -385,7 +385,7 @@
                                               XFA_ResolveFlag::kParent,
                                               XFA_ResolveFlag::kSiblings};
     absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
-        pScriptContext->ResolveObjects(pObject, wsName.AsStringView(), kFlags);
+        runtime->ResolveObjects(pObject, wsName.AsStringView(), kFlags);
     if (!maybeResult.has_value() ||
         !maybeResult.value().objects.front()->IsNode())
       continue;
@@ -402,8 +402,9 @@
 CJS_Result CJX_HostPseudoModel::beep(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+  if (!runtime->IsRunAtClient()) {
     return CJS_Result::Success();
+  }
 
   if (params.size() > 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -423,8 +424,9 @@
 CJS_Result CJX_HostPseudoModel::setFocus(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+  if (!runtime->IsRunAtClient()) {
     return CJS_Result::Success();
+  }
 
   if (params.size() != 1)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -438,8 +440,7 @@
     if (params[0]->IsObject()) {
       pNode = ToNode(runtime->ToXFAObject(params[0]));
     } else if (params[0]->IsString()) {
-      CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
-      CXFA_Object* pObject = pScriptContext->GetThisObject();
+      CXFA_Object* pObject = runtime->GetThisObject();
       if (!pObject)
         return CJS_Result::Success();
 
@@ -447,7 +448,7 @@
                                                 XFA_ResolveFlag::kParent,
                                                 XFA_ResolveFlag::kSiblings};
       absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
-          pScriptContext->ResolveObjects(
+          runtime->ResolveObjects(
               pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags);
       if (!maybeResult.has_value() ||
           !maybeResult.value().objects.front()->IsNode()) {
@@ -471,17 +472,15 @@
   if (!pNode)
     return CJS_Result::Success();
 
-  v8::Local<v8::Value> value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode);
-
-  return CJS_Result::Success(value);
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode));
 }
 
 CJS_Result CJX_HostPseudoModel::messageBox(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+  if (!runtime->IsRunAtClient()) {
     return CJS_Result::Success();
+  }
 
   if (params.empty() || params.size() > 4)
     return CJS_Result::Failure(JSMessage::kParamError);
@@ -526,8 +525,9 @@
 CJS_Result CJX_HostPseudoModel::print(
     CFXJSE_Engine* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
-  if (!GetDocument()->GetScriptContext()->IsRunAtClient())
+  if (!runtime->IsRunAtClient()) {
     return CJS_Result::Success();
+  }
 
   if (params.size() != 8)
     return CJS_Result::Failure(JSMessage::kParamError);
diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp
index 18faf93..f5b4d61 100644
--- a/fxjs/xfa/cjx_instancemanager.cpp
+++ b/fxjs/xfa/cjx_instancemanager.cpp
@@ -250,8 +250,7 @@
   }
 
   return CJS_Result::Success(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pNewInstance));
+      runtime->GetOrCreateJSBindingFromMap(pNewInstance));
 }
 
 CJS_Result CJX_InstanceManager::insertInstance(
@@ -291,8 +290,7 @@
   }
 
   return CJS_Result::Success(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pNewInstance));
+      runtime->GetOrCreateJSBindingFromMap(pNewInstance));
 }
 
 void CJX_InstanceManager::max(v8::Isolate* pIsolate,
diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp
index 21a767d..e94954e 100644
--- a/fxjs/xfa/cjx_model.cpp
+++ b/fxjs/xfa/cjx_model.cpp
@@ -65,11 +65,7 @@
     if (pNewNode->GetPacketType() == XFA_PacketType::Datasets)
       pNewNode->CreateXMLMappingNode();
   }
-
-  v8::Local<v8::Value> value =
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNewNode);
-
-  return CJS_Result::Success(value);
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNewNode));
 }
 
 CJS_Result CJX_Model::isCompatibleNS(
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index 0685107..9db88ba 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -137,9 +137,7 @@
     return CJS_Result::Failure(JSMessage::kParamError);
 
   CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0]));
-  return CJS_Result::Success(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-          pCloneNode));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pCloneNode));
 }
 
 CJS_Result CJX_Node::getAttribute(
@@ -169,8 +167,7 @@
   if (!pNode)
     return CJS_Result::Success(runtime->NewNull());
 
-  return CJS_Result::Success(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode));
 }
 
 CJS_Result CJX_Node::isPropertySpecified(
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index 9d6cdf9..7542e8a 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -159,14 +159,14 @@
 }
 
 CJS_Result CJX_Object::RunMethod(
+    CFXJSE_Engine* pScriptContext,
     const WideString& func,
     const std::vector<v8::Local<v8::Value>>& params) {
   auto it = method_specs_.find(func.ToUTF8());
   if (it == method_specs_.end())
     return CJS_Result::Failure(JSMessage::kUnknownMethod);
 
-  return it->second(this, GetXFAObject()->GetDocument()->GetScriptContext(),
-                    params);
+  return it->second(this, pScriptContext, params);
 }
 
 void CJX_Object::ThrowTooManyOccurrencesException(v8::Isolate* pIsolate,
@@ -241,8 +241,6 @@
     case XFA_AttributeType::Measure:
       SetMeasure(eAttr, CXFA_Measurement(wsValue.AsStringView()), bNotify);
       break;
-    default:
-      break;
   }
 }
 
@@ -301,8 +299,6 @@
         return absl::nullopt;
       return value->ToString();
     }
-    default:
-      break;
   }
   return absl::nullopt;
 }
@@ -464,13 +460,15 @@
     return;
   }
 
+  CFX_XMLElement* elem = ToXMLElement(xfaObj->GetXMLMappingNode());
+  if (!elem) {
+    return;
+  }
+
   WideString wsAttrName = WideString::FromASCII(XFA_AttributeToName(eAttr));
   if (eAttr == XFA_Attribute::ContentType)
     wsAttrName = L"xfa:" + wsAttrName;
-
-  CFX_XMLElement* elem = ToXMLElement(xfaObj->GetXMLMappingNode());
   elem->SetAttribute(wsAttrName, wsValue);
-  return;
 }
 
 void CJX_Object::SetAttributeValue(const WideString& wsValue,
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
index caa1a5e..51a2a13 100644
--- a/fxjs/xfa/cjx_object.h
+++ b/fxjs/xfa/cjx_object.h
@@ -16,7 +16,7 @@
 #include "fxjs/xfa/fxjse.h"
 #include "fxjs/xfa/jse_define.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "v8/include/cppgc/garbage-collected.h"
 #include "v8/include/cppgc/member.h"
 #include "v8/include/v8-forward.h"
@@ -119,7 +119,8 @@
   CXFA_LayoutItem* GetLayoutItem() const { return layout_item_; }
 
   bool HasMethod(const WideString& func) const;
-  CJS_Result RunMethod(const WideString& func,
+  CJS_Result RunMethod(CFXJSE_Engine* pScriptContext,
+                       const WideString& func,
                        const std::vector<v8::Local<v8::Value>>& params);
 
   bool HasAttribute(XFA_Attribute eAttr) const;
diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp
index ec562ce..daabd09 100644
--- a/fxjs/xfa/cjx_tree.cpp
+++ b/fxjs/xfa/cjx_tree.cpp
@@ -43,13 +43,12 @@
     return CJS_Result::Failure(JSMessage::kParamError);
 
   WideString wsExpression = runtime->ToWideString(params[0]);
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   CXFA_Object* pRefNode = GetXFAObject();
   if (pRefNode->GetElementType() == XFA_Element::Xfa)
-    pRefNode = pScriptContext->GetThisObject();
+    pRefNode = runtime->GetThisObject();
 
   absl::optional<CFXJSE_Engine::ResolveResult> maybeResult =
-      pScriptContext->ResolveObjects(
+      runtime->ResolveObjects(
           ToNode(pRefNode), wsExpression.AsStringView(),
           Mask<XFA_ResolveFlag>{
               XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
@@ -59,9 +58,8 @@
     return CJS_Result::Success(runtime->NewNull());
 
   if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) {
-    return CJS_Result::Success(
-        GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(
-            maybeResult.value().objects.front().Get()));
+    return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(
+        maybeResult.value().objects.front().Get()));
   }
 
   if (!maybeResult.value().script_attribute.callback ||
@@ -86,14 +84,13 @@
 
   CXFA_Object* refNode = GetXFAObject();
   if (refNode->GetElementType() == XFA_Element::Xfa)
-    refNode = GetDocument()->GetScriptContext()->GetThisObject();
+    refNode = runtime->GetThisObject();
 
-  CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext();
   const Mask<XFA_ResolveFlag> kFlags = {
       XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes,
       XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent,
       XFA_ResolveFlag::kSiblings};
-  return CJS_Result::Success(ResolveNodeList(pScriptContext->GetIsolate(),
+  return CJS_Result::Success(ResolveNodeList(runtime->GetIsolate(),
                                              runtime->ToWideString(params[0]),
                                              kFlags, ToNode(refNode)));
 }
diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp
index e2e4a21..a721349 100644
--- a/fxjs/xfa/cjx_treelist.cpp
+++ b/fxjs/xfa/cjx_treelist.cpp
@@ -44,6 +44,5 @@
   if (!pNode)
     return CJS_Result::Success();
 
-  return CJS_Result::Success(
-      GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode));
+  return CJS_Result::Success(runtime->GetOrCreateJSBindingFromMap(pNode));
 }
diff --git a/fxjs/xfa/fxjse.h b/fxjs/xfa/fxjse.h
index b020ce6..04d746b 100644
--- a/fxjs/xfa/fxjse.h
+++ b/fxjs/xfa/fxjse.h
@@ -10,6 +10,7 @@
 #include <stdint.h>
 
 #include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "v8/include/v8-forward.h"
 
 namespace pdfium {
@@ -78,7 +79,7 @@
 struct FXJSE_CLASS_DESCRIPTOR {
   const char* tag;  // `pdfium::fxjse::kClassTag` always.
   const char* name;
-  const FXJSE_FUNCTION_DESCRIPTOR* methods;
+  UNOWNED_PTR_EXCLUSION const FXJSE_FUNCTION_DESCRIPTOR* methods;
   int32_t methNum;
   FXJSE_PropTypeGetter dynPropTypeGetter;
   FXJSE_PropGetter dynPropGetter;
diff --git a/pdfium.gni b/pdfium.gni
index 9c6ae8d..ffa740a 100644
--- a/pdfium.gni
+++ b/pdfium.gni
@@ -41,7 +41,11 @@
   # standalone PDFium, and not when PDFium is embedded in our projects.
   pdf_use_cxx20 = (is_win && is_component_build) || is_fuchsia
 
-  # Build PDFium with PartitionAlloc as the memory allocator.
+  # Build PDFium against PartitionAlloc. When false, PDFium must build without
+  # requiring any PartitionAlloc headers or code to be present. When true,
+  # PDFium will use PartitionAlloc partitions to separate strings, scalars,
+  # etc. from other allocations. However, the use of PartitionAlloc for new or
+  # malloc is controlled by args in build_overrides/partition_alloc.gni.
   pdf_use_partition_alloc = pdf_use_partition_alloc_override
 
   # Build PDFium to use Skia (experimental) for all PDFium graphics.
@@ -49,7 +53,8 @@
   # renderer is selectable at runtime.
   pdf_use_skia = pdf_use_skia_override
 
-  # Build PDFium standalone
+  # Build PDFium standalone. Now only controls whether the test binaries
+  # are built. Most logic is conditioned by build_with_chromium.
   pdf_is_standalone = false
 
   # Build a complete static library
diff --git a/public/DEPS b/public/DEPS
index d0005ca..74caee4 100644
--- a/public/DEPS
+++ b/public/DEPS
@@ -1,8 +1,2 @@
-include_rules = [
-  # public/ needs to be standalone. Explicitly disallow everything.
-  '-core',
-  '-fpdfsdk',
-  '-testing',
-  '-third_party',
-  '-v8',
-]
+# public/ needs to be standalone; don't inherit any include rules.
+noparent = True
diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h
index 2bcf7f7..a0769b7 100644
--- a/public/fpdf_formfill.h
+++ b/public/fpdf_formfill.h
@@ -8,7 +8,7 @@
 #define PUBLIC_FPDF_FORMFILL_H_
 
 // clang-format off
-// NOLINTNEXTLINE(build/include)
+// NOLINTNEXTLINE(build/include_directory)
 #include "fpdfview.h"
 
 // These values are return values for a public API, so should not be changed
@@ -1913,15 +1913,15 @@
                                             int flags);
 
 #if defined(_SKIA_SUPPORT_)
-FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLRecord(FPDF_FORMHANDLE hHandle,
-                                              FPDF_RECORDER recorder,
-                                              FPDF_PAGE page,
-                                              int start_x,
-                                              int start_y,
-                                              int size_x,
-                                              int size_y,
-                                              int rotate,
-                                              int flags);
+FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDrawSkia(FPDF_FORMHANDLE hHandle,
+                                                FPDF_SKIA_CANVAS canvas,
+                                                FPDF_PAGE page,
+                                                int start_x,
+                                                int start_y,
+                                                int size_x,
+                                                int size_y,
+                                                int rotate,
+                                                int flags);
 #endif
 
 /*
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 206bfe2..0c901a7 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -4,6 +4,14 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+// This is the main header file for embedders of PDFium. It provides APIs to
+// initialize the library, load documents, and render pages, amongst other
+// things.
+//
+// NOTE: None of the PDFium APIs are thread-safe. They expect to be called
+// from a single thread. Barring that, embedders are required to ensure (via
+// a mutex or similar) that only a single PDFium call can be made at a time.
+//
 // NOTE: External docs refer to this file as "fpdfview.h", so do not rename
 // despite lack of consistency with other public files.
 
@@ -71,9 +79,9 @@
 typedef struct fpdf_pageobjectmark_t__* FPDF_PAGEOBJECTMARK;
 typedef const struct fpdf_pagerange_t__* FPDF_PAGERANGE;
 typedef const struct fpdf_pathsegment_t* FPDF_PATHSEGMENT;
-typedef void* FPDF_RECORDER;  // Passed into Skia as a SkPictureRecorder.
 typedef struct fpdf_schhandle_t__* FPDF_SCHHANDLE;
 typedef const struct fpdf_signature_t__* FPDF_SIGNATURE;
+typedef void* FPDF_SKIA_CANVAS;  // Passed into Skia as an SkCanvas.
 typedef struct fpdf_structelement_t__* FPDF_STRUCTELEMENT;
 typedef const struct fpdf_structelement_attr_t__* FPDF_STRUCTELEMENT_ATTR;
 typedef struct fpdf_structtree_t__* FPDF_STRUCTTREE;
@@ -98,12 +106,14 @@
 // String types
 typedef unsigned short FPDF_WCHAR;
 
-// FPDFSDK may use three types of strings: byte string, wide string (UTF-16LE
-// encoded), and platform dependent string
+// The public PDFium API uses three types of strings: byte string, wide string
+// (UTF-16LE encoded), and platform dependent string.
+
+// Public PDFium API type for byte strings.
 typedef const char* FPDF_BYTESTRING;
 
-// FPDFSDK always uses UTF-16LE encoded wide strings, each character uses 2
-// bytes (except surrogation), with the low byte first.
+// The public PDFium API always uses UTF-16LE encoded wide strings, each
+// character uses 2 bytes (except surrogation), with the low byte first.
 typedef const FPDF_WCHAR* FPDF_WIDESTRING;
 
 // Structure for persisting a string beyond the duration of a callback.
@@ -222,18 +232,6 @@
 extern "C" {
 #endif
 
-// Function: FPDF_InitLibrary
-//          Initialize the FPDFSDK library
-// Parameters:
-//          None
-// Return value:
-//          None.
-// Comments:
-//          Convenience function to call FPDF_InitLibraryWithConfig() for
-//          backwards compatibility purposes. This will be deprecated in the
-//          future.
-FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary();
-
 // PDF renderer types - Experimental.
 // Selection of 2D graphics library to use for rendering to FPDF_BITMAPs.
 typedef enum {
@@ -280,11 +278,10 @@
   // corresponding render library is not included in the build will similarly
   // fail with an immediate crash.
   FPDF_RENDERER_TYPE m_RendererType;
-
 } FPDF_LIBRARY_CONFIG;
 
 // Function: FPDF_InitLibraryWithConfig
-//          Initialize the FPDFSDK library
+//          Initialize the PDFium library and allocate global resources for it.
 // Parameters:
 //          config - configuration information as above.
 // Return value:
@@ -295,17 +292,33 @@
 FPDF_EXPORT void FPDF_CALLCONV
 FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* config);
 
-// Function: FPDF_DestroyLibary
-//          Release all resources allocated by the FPDFSDK library.
+// Function: FPDF_InitLibrary
+//          Initialize the PDFium library (alternative form).
+// Parameters:
+//          None
+// Return value:
+//          None.
+// Comments:
+//          Convenience function to call FPDF_InitLibraryWithConfig() with a
+//          default configuration for backwards compatibility purposes. New
+//          code should call FPDF_InitLibraryWithConfig() instead. This will
+//          be deprecated in the future.
+FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary();
+
+// Function: FPDF_DestroyLibrary
+//          Release global resources allocated to the PDFium library by
+//          FPDF_InitLibrary() or FPDF_InitLibraryWithConfig().
 // Parameters:
 //          None.
 // Return value:
 //          None.
 // Comments:
-//          You can call this function to release all memory blocks allocated by
-//          the library.
-//          After this function is called, you should not call any PDF
+//          After this function is called, you must not call any PDF
 //          processing functions.
+//
+//          Calling this function does not automatically close other
+//          objects. It is recommended to close other objects before
+//          closing the library with this function.
 FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyLibrary();
 
 // Policy for accessing the local machine time.
@@ -435,7 +448,7 @@
   // Position is specified by byte offset from the beginning of the file.
   // The pointer to the buffer is never NULL and the size is never 0.
   // The position and size will never go out of range of the file length.
-  // It may be possible for FPDFSDK to call this function multiple times for
+  // It may be possible for PDFium to call this function multiple times for
   // the same position.
   // Return value: should be non-zero if successful, zero for error.
   int (*m_GetBlock)(void* param,
@@ -912,18 +925,19 @@
 
 #if defined(_SKIA_SUPPORT_)
 // Experimental API.
-// Function: FPDF_RenderPageSkp
-//          Render contents of a page to a Skia SkPictureRecorder.
+// Function: FPDF_RenderPageSkia
+//          Render contents of a page to a Skia SkCanvas.
 // Parameters:
+//          canvas      -   SkCanvas to render to.
 //          page        -   Handle to the page.
 //          size_x      -   Horizontal size (in pixels) for displaying the page.
 //          size_y      -   Vertical size (in pixels) for displaying the page.
 // Return value:
-//          The SkPictureRecorder that holds the rendering of the page, or NULL
-//          on failure. Caller takes ownership of the returned result.
-FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
-                                                           int size_x,
-                                                           int size_y);
+//          None.
+FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageSkia(FPDF_SKIA_CANVAS canvas,
+                                                   FPDF_PAGE page,
+                                                   int size_x,
+                                                   int size_y);
 #endif
 
 // Function: FPDF_ClosePage
diff --git a/samples/BUILD.gn b/samples/BUILD.gn
index ccf7ff8..981750b 100644
--- a/samples/BUILD.gn
+++ b/samples/BUILD.gn
@@ -21,7 +21,9 @@
   if (is_asan) {
     defines += [ "PDF_ENABLE_ASAN" ]
   }
-
+  if (pdf_use_partition_alloc) {
+    defines += [ "PDF_USE_PARTITION_ALLOC" ]
+  }
   if (enable_callgrind) {
     defines += [ "ENABLE_CALLGRIND" ]
   }
@@ -33,13 +35,15 @@
 executable("pdfium_test") {
   testonly = true
   sources = [
+    "helpers/dump.cc",
+    "helpers/dump.h",
+    "helpers/event.cc",
+    "helpers/event.h",
+    "helpers/page_renderer.cc",
+    "helpers/page_renderer.h",
+    "helpers/write.cc",
+    "helpers/write.h",
     "pdfium_test.cc",
-    "pdfium_test_dump_helper.cc",
-    "pdfium_test_dump_helper.h",
-    "pdfium_test_event_helper.cc",
-    "pdfium_test_event_helper.h",
-    "pdfium_test_write_helper.cc",
-    "pdfium_test_write_helper.h",
   ]
 
   # Note: One should write programs that depend on ../:pdfium. Whereas this
@@ -55,6 +59,13 @@
   ]
   configs += [ ":pdfium_samples_config" ]
 
+  if (is_win) {
+    sources += [
+      "helpers/win32/com_factory.cc",
+      "helpers/win32/com_factory.h",
+    ]
+  }
+
   if (pdf_enable_v8) {
     deps += [
       "//v8:v8_headers",
diff --git a/samples/pdfium_test_dump_helper.cc b/samples/helpers/dump.cc
similarity index 96%
rename from samples/pdfium_test_dump_helper.cc
rename to samples/helpers/dump.cc
index 4d45390..e2a8469 100644
--- a/samples/pdfium_test_dump_helper.cc
+++ b/samples/helpers/dump.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "samples/pdfium_test_dump_helper.h"
+#include "samples/helpers/dump.h"
 
 #include <limits.h>
 #include <string.h>
@@ -98,15 +98,17 @@
   static const size_t kBufSize = 1024;
   unsigned short buf[kBufSize];
   unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
-  if (len > 0)
+  if (len > 0) {
     printf("%*s S: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str());
+  }
 
   int attr_count = FPDF_StructElement_GetAttributeCount(child);
   for (int i = 0; i < attr_count; i++) {
     FPDF_STRUCTELEMENT_ATTR child_attr =
         FPDF_StructElement_GetAttributeAtIndex(child, i);
-    if (!child_attr)
+    if (!child_attr) {
       continue;
+    }
     printf("%*s A[%d]:\n", indent * 2, "", i);
     DumpStructureElementAttributes(child_attr, indent * 2 + 2);
   }
@@ -127,8 +129,9 @@
 
   memset(buf, 0, sizeof(buf));
   len = FPDF_StructElement_GetID(child, buf, kBufSize);
-  if (len > 0)
+  if (len > 0) {
     printf("%*s ID: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str());
+  }
 
   memset(buf, 0, sizeof(buf));
   len = FPDF_StructElement_GetLang(child, buf, kBufSize);
@@ -138,8 +141,9 @@
   }
 
   int mcid = FPDF_StructElement_GetMarkedContentID(child);
-  if (mcid != -1)
+  if (mcid != -1) {
     printf("%*s MCID: %d\n", indent * 2, "", mcid);
+  }
 
   FPDF_STRUCTELEMENT parent = FPDF_StructElement_GetParent(child);
   if (parent) {
@@ -169,8 +173,9 @@
     FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
     // If the child is not an Element then this will return null. This can
     // happen if the element is things like an object reference or a stream.
-    if (!sub_child)
+    if (!sub_child) {
       continue;
+    }
 
     DumpChildStructure(sub_child, indent + 1);
   }
@@ -211,8 +216,9 @@
     char meta_buffer[4096];
     unsigned long len =
         FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
-    if (!len)
+    if (!len) {
       continue;
+    }
 
     auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
     printf("%-12s = %ls (%lu bytes)\n", meta_tag,
diff --git a/samples/pdfium_test_dump_helper.h b/samples/helpers/dump.h
similarity index 73%
rename from samples/pdfium_test_dump_helper.h
rename to samples/helpers/dump.h
index 727731e..6682b29 100644
--- a/samples/pdfium_test_dump_helper.h
+++ b/samples/helpers/dump.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
+#ifndef SAMPLES_HELPERS_DUMP_H_
+#define SAMPLES_HELPERS_DUMP_H_
 
 #include "public/fpdfview.h"
 
@@ -12,4 +12,4 @@
 void DumpPageStructure(FPDF_PAGE page, int page_idx);
 void DumpMetaData(FPDF_DOCUMENT doc);
 
-#endif  // SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
+#endif  // SAMPLES_HELPERS_DUMP_H_
diff --git a/samples/pdfium_test_event_helper.cc b/samples/helpers/event.cc
similarity index 92%
rename from samples/pdfium_test_event_helper.cc
rename to samples/helpers/event.cc
index d55057e..48491e8 100644
--- a/samples/pdfium_test_event_helper.cc
+++ b/samples/helpers/event.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "samples/pdfium_test_event_helper.h"
+#include "samples/helpers/event.h"
 
 #include <stdio.h>
 
@@ -17,12 +17,15 @@
 
 uint32_t GetModifiers(std::string modifiers_string) {
   uint32_t modifiers = 0;
-  if (modifiers_string.find("shift") != std::string::npos)
+  if (modifiers_string.find("shift") != std::string::npos) {
     modifiers |= FWL_EVENTFLAG_ShiftKey;
-  if (modifiers_string.find("control") != std::string::npos)
+  }
+  if (modifiers_string.find("control") != std::string::npos) {
     modifiers |= FWL_EVENTFLAG_ControlKey;
-  if (modifiers_string.find("alt") != std::string::npos)
+  }
+  if (modifiers_string.find("alt") != std::string::npos) {
     modifiers |= FWL_EVENTFLAG_AltKey;
+  }
 
   return modifiers;
 }
@@ -65,12 +68,13 @@
   int y = atoi(tokens[3].c_str());
   uint32_t modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
 
-  if (tokens[1] == "left")
+  if (tokens[1] == "left") {
     FORM_OnLButtonDown(form, page, modifiers, x, y);
-  else if (tokens[1] == "right")
+  } else if (tokens[1] == "right") {
     FORM_OnRButtonDown(form, page, modifiers, x, y);
-  else
+  } else {
     fprintf(stderr, "mousedown: bad button name\n");
+  }
 }
 
 void SendMouseUpEvent(FPDF_FORMHANDLE form,
@@ -84,12 +88,13 @@
   int x = atoi(tokens[2].c_str());
   int y = atoi(tokens[3].c_str());
   int modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
-  if (tokens[1] == "left")
+  if (tokens[1] == "left") {
     FORM_OnLButtonUp(form, page, modifiers, x, y);
-  else if (tokens[1] == "right")
+  } else if (tokens[1] == "right") {
     FORM_OnRButtonUp(form, page, modifiers, x, y);
-  else
+  } else {
     fprintf(stderr, "mouseup: bad button name\n");
+  }
 }
 
 void SendMouseDoubleClickEvent(FPDF_FORMHANDLE form,
@@ -161,8 +166,9 @@
   auto lines = StringSplit(events, '\n');
   for (const auto& line : lines) {
     auto command = StringSplit(line, '#');
-    if (command[0].empty())
+    if (command[0].empty()) {
       continue;
+    }
     auto tokens = StringSplit(command[0], ',');
     if (tokens[0] == "charcode") {
       SendCharCodeEvent(form, page, tokens);
diff --git a/samples/pdfium_test_event_helper.h b/samples/helpers/event.h
similarity index 75%
rename from samples/pdfium_test_event_helper.h
rename to samples/helpers/event.h
index 2f2825a..021c075 100644
--- a/samples/pdfium_test_event_helper.h
+++ b/samples/helpers/event.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
+#ifndef SAMPLES_HELPERS_EVENT_H_
+#define SAMPLES_HELPERS_EVENT_H_
 
 #include <functional>
 #include <string>
@@ -16,4 +16,4 @@
                     const std::string& events,
                     const std::function<void()>& idler);
 
-#endif  // SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
+#endif  // SAMPLES_HELPERS_EVENT_H_
diff --git a/samples/helpers/page_renderer.cc b/samples/helpers/page_renderer.cc
new file mode 100644
index 0000000..e7771ef
--- /dev/null
+++ b/samples/helpers/page_renderer.cc
@@ -0,0 +1,16 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/helpers/page_renderer.h"
+
+#include "public/fpdfview.h"
+
+PageRenderer::PageRenderer(FPDF_PAGE page, int width, int height, int flags)
+    : page_(page), width_(width), height_(height), flags_(flags) {}
+
+PageRenderer::~PageRenderer() = default;
+
+bool PageRenderer::Continue() {
+  return false;
+}
diff --git a/samples/helpers/page_renderer.h b/samples/helpers/page_renderer.h
new file mode 100644
index 0000000..277bd0f
--- /dev/null
+++ b/samples/helpers/page_renderer.h
@@ -0,0 +1,47 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_HELPERS_PAGE_RENDERER_H_
+#define SAMPLES_HELPERS_PAGE_RENDERER_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+
+// Renderer for a single page.
+class PageRenderer {
+ public:
+  virtual ~PageRenderer();
+
+  // Returns `true` if the rendered output exists. Must call `Finish()` first.
+  virtual bool HasOutput() const = 0;
+
+  // Starts rendering the page, returning `false` on failure.
+  virtual bool Start() = 0;
+
+  // Continues rendering the page, returning `false` when complete.
+  virtual bool Continue();
+
+  // Finishes rendering the page.
+  virtual void Finish(FPDF_FORMHANDLE form) = 0;
+
+  // Writes rendered output to a file, returning `false` on failure.
+  virtual bool Write(const std::string& name, int page_index, bool md5) = 0;
+
+ protected:
+  PageRenderer(FPDF_PAGE page, int width, int height, int flags);
+
+  FPDF_PAGE page() { return page_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int flags() const { return flags_; }
+
+ private:
+  FPDF_PAGE page_;
+  int width_;
+  int height_;
+  int flags_;
+};
+
+#endif  // SAMPLES_HELPERS_PAGE_RENDERER_H_
diff --git a/samples/helpers/win32/com_factory.cc b/samples/helpers/win32/com_factory.cc
new file mode 100644
index 0000000..7c5fe44
--- /dev/null
+++ b/samples/helpers/win32/com_factory.cc
@@ -0,0 +1,51 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/helpers/win32/com_factory.h"
+
+#include <combaseapi.h>
+#include <objbase.h>
+#include <winerror.h>
+#include <xpsobjectmodel.h>
+
+#include "third_party/base/check_op.h"
+
+ComFactory::ComFactory() = default;
+
+ComFactory::~ComFactory() {
+  if (xps_om_object_factory_) {
+    xps_om_object_factory_->Release();
+  }
+
+  if (initialized_) {
+    CoUninitialize();
+  }
+}
+
+bool ComFactory::Initialize() {
+  if (!initialized_) {
+    HRESULT result =
+        CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
+    DCHECK_NE(RPC_E_CHANGED_MODE, result);
+    initialized_ = SUCCEEDED(result);
+  }
+
+  return initialized_;
+}
+
+IXpsOMObjectFactory* ComFactory::GetXpsOMObjectFactory() {
+  if (!xps_om_object_factory_ && Initialize()) {
+    HRESULT result =
+        CoCreateInstance(__uuidof(XpsOMObjectFactory), /*pUnkOuter=*/nullptr,
+                         CLSCTX_INPROC_SERVER, __uuidof(IXpsOMObjectFactory),
+                         reinterpret_cast<LPVOID*>(&xps_om_object_factory_));
+    if (SUCCEEDED(result)) {
+      DCHECK(xps_om_object_factory_);
+    } else {
+      DCHECK(!xps_om_object_factory_);
+    }
+  }
+
+  return xps_om_object_factory_;
+}
diff --git a/samples/helpers/win32/com_factory.h b/samples/helpers/win32/com_factory.h
new file mode 100644
index 0000000..eabaffe
--- /dev/null
+++ b/samples/helpers/win32/com_factory.h
@@ -0,0 +1,25 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_HELPERS_WIN32_COM_FACTORY_H_
+#define SAMPLES_HELPERS_WIN32_COM_FACTORY_H_
+
+struct IXpsOMObjectFactory;
+
+// Factory for COM instances.
+class ComFactory final {
+ public:
+  ComFactory();
+  ~ComFactory();
+
+  IXpsOMObjectFactory* GetXpsOMObjectFactory();
+
+ private:
+  bool Initialize();
+
+  bool initialized_ = false;
+  IXpsOMObjectFactory* xps_om_object_factory_ = nullptr;
+};
+
+#endif  // SAMPLES_HELPERS_WIN32_COM_FACTORY_H_
diff --git a/samples/pdfium_test_write_helper.cc b/samples/helpers/write.cc
similarity index 87%
rename from samples/pdfium_test_write_helper.cc
rename to samples/helpers/write.cc
index 7ac49d7..13223d2 100644
--- a/samples/pdfium_test_write_helper.cc
+++ b/samples/helpers/write.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "samples/pdfium_test_write_helper.h"
+#include "samples/helpers/write.h"
 
 #include <limits.h>
 
@@ -28,114 +28,156 @@
 namespace {
 
 bool CheckDimensions(int stride, int width, int height) {
-  if (stride < 0 || width < 0 || height < 0)
+  if (stride < 0 || width < 0 || height < 0) {
     return false;
-  if (height > 0 && stride > INT_MAX / height)
+  }
+  if (height > 0 && stride > INT_MAX / height) {
     return false;
+  }
   return true;
 }
 
 const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
-  if (subtype == FPDF_ANNOT_TEXT)
+  if (subtype == FPDF_ANNOT_TEXT) {
     return "Text";
-  if (subtype == FPDF_ANNOT_LINK)
+  }
+  if (subtype == FPDF_ANNOT_LINK) {
     return "Link";
-  if (subtype == FPDF_ANNOT_FREETEXT)
+  }
+  if (subtype == FPDF_ANNOT_FREETEXT) {
     return "FreeText";
-  if (subtype == FPDF_ANNOT_LINE)
+  }
+  if (subtype == FPDF_ANNOT_LINE) {
     return "Line";
-  if (subtype == FPDF_ANNOT_SQUARE)
+  }
+  if (subtype == FPDF_ANNOT_SQUARE) {
     return "Square";
-  if (subtype == FPDF_ANNOT_CIRCLE)
+  }
+  if (subtype == FPDF_ANNOT_CIRCLE) {
     return "Circle";
-  if (subtype == FPDF_ANNOT_POLYGON)
+  }
+  if (subtype == FPDF_ANNOT_POLYGON) {
     return "Polygon";
-  if (subtype == FPDF_ANNOT_POLYLINE)
+  }
+  if (subtype == FPDF_ANNOT_POLYLINE) {
     return "PolyLine";
-  if (subtype == FPDF_ANNOT_HIGHLIGHT)
+  }
+  if (subtype == FPDF_ANNOT_HIGHLIGHT) {
     return "Highlight";
-  if (subtype == FPDF_ANNOT_UNDERLINE)
+  }
+  if (subtype == FPDF_ANNOT_UNDERLINE) {
     return "Underline";
-  if (subtype == FPDF_ANNOT_SQUIGGLY)
+  }
+  if (subtype == FPDF_ANNOT_SQUIGGLY) {
     return "Squiggly";
-  if (subtype == FPDF_ANNOT_STRIKEOUT)
+  }
+  if (subtype == FPDF_ANNOT_STRIKEOUT) {
     return "StrikeOut";
-  if (subtype == FPDF_ANNOT_STAMP)
+  }
+  if (subtype == FPDF_ANNOT_STAMP) {
     return "Stamp";
-  if (subtype == FPDF_ANNOT_CARET)
+  }
+  if (subtype == FPDF_ANNOT_CARET) {
     return "Caret";
-  if (subtype == FPDF_ANNOT_INK)
+  }
+  if (subtype == FPDF_ANNOT_INK) {
     return "Ink";
-  if (subtype == FPDF_ANNOT_POPUP)
+  }
+  if (subtype == FPDF_ANNOT_POPUP) {
     return "Popup";
-  if (subtype == FPDF_ANNOT_FILEATTACHMENT)
+  }
+  if (subtype == FPDF_ANNOT_FILEATTACHMENT) {
     return "FileAttachment";
-  if (subtype == FPDF_ANNOT_SOUND)
+  }
+  if (subtype == FPDF_ANNOT_SOUND) {
     return "Sound";
-  if (subtype == FPDF_ANNOT_MOVIE)
+  }
+  if (subtype == FPDF_ANNOT_MOVIE) {
     return "Movie";
-  if (subtype == FPDF_ANNOT_WIDGET)
+  }
+  if (subtype == FPDF_ANNOT_WIDGET) {
     return "Widget";
-  if (subtype == FPDF_ANNOT_SCREEN)
+  }
+  if (subtype == FPDF_ANNOT_SCREEN) {
     return "Screen";
-  if (subtype == FPDF_ANNOT_PRINTERMARK)
+  }
+  if (subtype == FPDF_ANNOT_PRINTERMARK) {
     return "PrinterMark";
-  if (subtype == FPDF_ANNOT_TRAPNET)
+  }
+  if (subtype == FPDF_ANNOT_TRAPNET) {
     return "TrapNet";
-  if (subtype == FPDF_ANNOT_WATERMARK)
+  }
+  if (subtype == FPDF_ANNOT_WATERMARK) {
     return "Watermark";
-  if (subtype == FPDF_ANNOT_THREED)
+  }
+  if (subtype == FPDF_ANNOT_THREED) {
     return "3D";
-  if (subtype == FPDF_ANNOT_RICHMEDIA)
+  }
+  if (subtype == FPDF_ANNOT_RICHMEDIA) {
     return "RichMedia";
-  if (subtype == FPDF_ANNOT_XFAWIDGET)
+  }
+  if (subtype == FPDF_ANNOT_XFAWIDGET) {
     return "XFAWidget";
-  NOTREACHED();
-  return "";
+  }
+  NOTREACHED_NORETURN();
 }
 
 void AppendFlagString(const char* flag, std::string* output) {
-  if (!output->empty())
+  if (!output->empty()) {
     *output += ", ";
+  }
   *output += flag;
 }
 
 std::string AnnotFlagsToString(int flags) {
   std::string str;
-  if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
+  if (flags & FPDF_ANNOT_FLAG_INVISIBLE) {
     AppendFlagString("Invisible", &str);
-  if (flags & FPDF_ANNOT_FLAG_HIDDEN)
+  }
+  if (flags & FPDF_ANNOT_FLAG_HIDDEN) {
     AppendFlagString("Hidden", &str);
-  if (flags & FPDF_ANNOT_FLAG_PRINT)
+  }
+  if (flags & FPDF_ANNOT_FLAG_PRINT) {
     AppendFlagString("Print", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOZOOM)
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOZOOM) {
     AppendFlagString("NoZoom", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOROTATE)
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOROTATE) {
     AppendFlagString("NoRotate", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOVIEW)
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOVIEW) {
     AppendFlagString("NoView", &str);
-  if (flags & FPDF_ANNOT_FLAG_READONLY)
+  }
+  if (flags & FPDF_ANNOT_FLAG_READONLY) {
     AppendFlagString("ReadOnly", &str);
-  if (flags & FPDF_ANNOT_FLAG_LOCKED)
+  }
+  if (flags & FPDF_ANNOT_FLAG_LOCKED) {
     AppendFlagString("Locked", &str);
-  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
+  }
+  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW) {
     AppendFlagString("ToggleNoView", &str);
+  }
   return str;
 }
 
 const char* PageObjectTypeToCString(int type) {
-  if (type == FPDF_PAGEOBJ_TEXT)
+  if (type == FPDF_PAGEOBJ_TEXT) {
     return "Text";
-  if (type == FPDF_PAGEOBJ_PATH)
+  }
+  if (type == FPDF_PAGEOBJ_PATH) {
     return "Path";
-  if (type == FPDF_PAGEOBJ_IMAGE)
+  }
+  if (type == FPDF_PAGEOBJ_IMAGE) {
     return "Image";
-  if (type == FPDF_PAGEOBJ_SHADING)
+  }
+  if (type == FPDF_PAGEOBJ_SHADING) {
     return "Shading";
-  if (type == FPDF_PAGEOBJ_FORM)
+  }
+  if (type == FPDF_PAGEOBJ_FORM) {
     return "Form";
-  NOTREACHED();
-  return "";
+  }
+  NOTREACHED_NORETURN();
 }
 
 std::vector<uint8_t> EncodePng(pdfium::span<const uint8_t> input,
@@ -162,7 +204,7 @@
                                           /*discard_transparency=*/false);
       break;
     default:
-      NOTREACHED();
+      NOTREACHED_NORETURN();
   }
   return png;
 }
@@ -521,8 +563,9 @@
     return;
   }
   FILE* fp = fopen(filename.c_str(), "wb");
-  if (!fp)
+  if (!fp) {
     return;
+  }
 
   HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
 
@@ -534,8 +577,9 @@
   std::vector<const ENHMETARECORD*> items;
   EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
   for (const ENHMETARECORD* record : items) {
-    if (record->iType != EMR_GDICOMMENT)
+    if (record->iType != EMR_GDICOMMENT) {
       continue;
+    }
 
     const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
     const char* data = reinterpret_cast<const char*>(comment->Data);
@@ -551,16 +595,43 @@
 #endif  // _WIN32
 
 #ifdef PDF_ENABLE_SKIA
-std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture) {
-  std::string filename = GeneratePageOutputFilename(pdf_name, num, "skp");
+std::unique_ptr<SkWStream> WriteToSkWStream(const std::string& pdf_name,
+                                            int num,
+                                            const std::string& extension) {
+  std::string discarded_filename;
+  return WriteToSkWStream(pdf_name, num, extension, discarded_filename);
+}
+
+std::unique_ptr<SkWStream> WriteToSkWStream(const std::string& pdf_name,
+                                            int num,
+                                            const std::string& extension,
+                                            std::string& filename) {
+  filename =
+      GeneratePageOutputFilename(pdf_name.c_str(), num, extension.c_str());
   if (filename.empty()) {
-    return filename;
+    return nullptr;
   }
-  SkFILEWStream wStream(filename.c_str());
-  picture.serialize(&wStream);
+
+  auto stream = std::make_unique<SkFILEWStream>(filename.c_str());
+  if (!stream->isValid()) {
+    return nullptr;
+  }
+
+  return stream;
+}
+
+std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture) {
+  std::string filename;
+  std::unique_ptr<SkWStream> stream =
+      WriteToSkWStream(pdf_name, num, "skp", filename);
+  if (!stream) {
+    return "";
+  }
+
+  picture.serialize(stream.get());
   return filename;
 }
-#endif
+#endif  // PDF_ENABLE_SKIA
 
 enum class ThumbnailDecodeType { kBitmap, kRawStream, kDecodedStream };
 
@@ -604,24 +675,27 @@
   }
 
   size_t bytes_written = fwrite(buf, 1, buflen, fp);
-  if (bytes_written == buflen)
+  if (bytes_written == buflen) {
     fprintf(stderr, "Successfully wrote %s %s.\n", filetype, filename);
-  else
+  } else {
     fprintf(stderr, "Failed to write to %s.\n", filename);
+  }
   fclose(fp);
 }
 
 std::vector<uint8_t> EncodeBitmapToPng(ScopedFPDFBitmap bitmap) {
   std::vector<uint8_t> png_encoding;
   int format = FPDFBitmap_GetFormat(bitmap.get());
-  if (format == FPDFBitmap_Unknown)
+  if (format == FPDFBitmap_Unknown) {
     return png_encoding;
+  }
 
   int width = FPDFBitmap_GetWidth(bitmap.get());
   int height = FPDFBitmap_GetHeight(bitmap.get());
   int stride = FPDFBitmap_GetStride(bitmap.get());
-  if (!CheckDimensions(stride, width, height))
+  if (!CheckDimensions(stride, width, height)) {
     return png_encoding;
+  }
 
   auto input = pdfium::make_span(
       static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap.get())),
@@ -642,8 +716,9 @@
       std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
       unsigned long actual_length_bytes =
           FPDFAttachment_GetName(attachment, buf.data(), length_bytes);
-      if (actual_length_bytes == length_bytes)
+      if (actual_length_bytes == length_bytes) {
         attachment_name = GetPlatformString(buf.data());
+      }
     }
     if (attachment_name.empty()) {
       fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
diff --git a/samples/pdfium_test_write_helper.h b/samples/helpers/write.h
similarity index 74%
rename from samples/pdfium_test_write_helper.h
rename to samples/helpers/write.h
index ffeba1f..b23760e 100644
--- a/samples/pdfium_test_write_helper.h
+++ b/samples/helpers/write.h
@@ -2,16 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
+#ifndef SAMPLES_HELPERS_WRITE_H_
+#define SAMPLES_HELPERS_WRITE_H_
 
+#include <memory>
 #include <string>
 
 #include "public/fpdfview.h"
 
 #ifdef PDF_ENABLE_SKIA
 class SkPicture;
-#endif
+class SkWStream;
+#endif  // PDF_ENABLE_SKIA
 
 std::string WritePpm(const char* pdf_name,
                      int num,
@@ -40,6 +42,13 @@
 #endif  // _WIN32
 
 #ifdef PDF_ENABLE_SKIA
+std::unique_ptr<SkWStream> WriteToSkWStream(const std::string& pdf_name,
+                                            int num,
+                                            const std::string& extension);
+std::unique_ptr<SkWStream> WriteToSkWStream(const std::string& pdf_name,
+                                            int num,
+                                            const std::string& extension,
+                                            std::string& filename);
 std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture);
 #endif  // PDF_ENABLE_SKIA
 
@@ -57,4 +66,4 @@
                              int page_num);
 void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num);
 
-#endif  // SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
+#endif  // SAMPLES_HELPERS_WRITE_H_
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index 2693a56..282c539 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include <locale.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -30,9 +32,10 @@
 #include "public/fpdf_structtree.h"
 #include "public/fpdf_text.h"
 #include "public/fpdfview.h"
-#include "samples/pdfium_test_dump_helper.h"
-#include "samples/pdfium_test_event_helper.h"
-#include "samples/pdfium_test_write_helper.h"
+#include "samples/helpers/dump.h"
+#include "samples/helpers/event.h"
+#include "samples/helpers/page_renderer.h"
+#include "samples/helpers/write.h"
 #include "testing/command_line_helpers.h"
 #include "testing/font_renamer.h"
 #include "testing/fx_string_testhelpers.h"
@@ -41,11 +44,16 @@
 #include "testing/utils/hash.h"
 #include "testing/utils/path_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/check_op.h"
 
 #ifdef _WIN32
 #include <crtdbg.h>
 #include <errhandlingapi.h>
 #include <io.h>
+#include <wingdi.h>
+
+#include "samples/helpers/win32/com_factory.h"
+#include "third_party/base/win/scoped_select_object.h"
 #else
 #include <unistd.h>
 #endif  // _WIN32
@@ -54,15 +62,25 @@
 #include <valgrind/callgrind.h>
 #endif  // ENABLE_CALLGRIND
 
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "testing/allocator_shim_config.h"
+#endif
+
 #ifdef PDF_ENABLE_SKIA
 #include "third_party/skia/include/core/SkCanvas.h"           // nogncheck
 #include "third_party/skia/include/core/SkColor.h"            // nogncheck
+#include "third_party/skia/include/core/SkDocument.h"         // nogncheck
 #include "third_party/skia/include/core/SkPicture.h"          // nogncheck
 #include "third_party/skia/include/core/SkPictureRecorder.h"  // nogncheck
 #include "third_party/skia/include/core/SkPixmap.h"           // nogncheck
 #include "third_party/skia/include/core/SkRefCnt.h"           // nogncheck
+#include "third_party/skia/include/core/SkStream.h"           // nogncheck
 #include "third_party/skia/include/core/SkSurface.h"          // nogncheck
 
+#ifdef _WIN32
+#include "third_party/skia/include/docs/SkXPSDocument.h"  // nogncheck
+#endif
+
 #ifdef BUILD_WITH_CHROMIUM
 #include "samples/chromium_support/discardable_memory_allocator.h"  // nogncheck
 #endif
@@ -95,6 +113,17 @@
 
 namespace {
 
+enum class RendererType {
+  kDefault,
+  kAgg,
+#ifdef _WIN32
+  kGdi,
+#endif  // _WIN32
+#if defined(PDF_ENABLE_SKIA)
+  kSkia,
+#endif  // defined(PDF_ENABLE_SKIA)
+};
+
 enum class OutputFormat {
   kNone,
   kPageInfo,
@@ -112,7 +141,10 @@
 #endif
 #ifdef PDF_ENABLE_SKIA
   kSkp,
-#endif
+#ifdef _WIN32
+  kXps,
+#endif  // _WIN32
+#endif  // PDF_ENABLE_SKIA
 };
 
 struct Options {
@@ -141,7 +173,7 @@
   bool save_thumbnails = false;
   bool save_thumbnails_decoded = false;
   bool save_thumbnails_raw = false;
-  absl::optional<FPDF_RENDERER_TYPE> use_renderer_type;
+  RendererType use_renderer_type = RendererType::kDefault;
 #ifdef PDF_ENABLE_V8
   bool disable_javascript = false;
   std::string js_flags;  // Extra flags to pass to v8 init.
@@ -486,23 +518,25 @@
       options->save_thumbnails_decoded = true;
     } else if (cur_arg == "--save-thumbs-raw") {
       options->save_thumbnails_raw = true;
-#if defined(_SKIA_SUPPORT_)
     } else if (ParseSwitchKeyValue(cur_arg, "--use-renderer=", &value)) {
-      if (options->use_renderer_type.has_value()) {
+      if (options->use_renderer_type != RendererType::kDefault) {
         fprintf(stderr, "Duplicate --use-renderer argument\n");
         return false;
       }
       if (value == "agg") {
-        options->use_renderer_type = FPDF_RENDERERTYPE_AGG;
+        options->use_renderer_type = RendererType::kAgg;
+#ifdef _WIN32
+      } else if (value == "gdi") {
+        options->use_renderer_type = RendererType::kGdi;
+#endif  // _WIN32
+#if defined(PDF_ENABLE_SKIA)
       } else if (value == "skia") {
-        options->use_renderer_type = FPDF_RENDERERTYPE_SKIA;
+        options->use_renderer_type = RendererType::kSkia;
+#endif  // defined(PDF_ENABLE_SKIA)
       } else {
-        fprintf(stderr,
-                "Invalid --use-renderer argument, value must be one of agg or "
-                "skia\n");
+        fprintf(stderr, "Invalid --use-renderer argument\n");
         return false;
       }
-#endif  // defined(_SKIA_SUPPORT_)
 #ifdef PDF_ENABLE_V8
     } else if (cur_arg == "--disable-javascript") {
       options->disable_javascript = true;
@@ -558,6 +592,14 @@
         return false;
       }
       options->output_format = OutputFormat::kSkp;
+#ifdef _WIN32
+    } else if (cur_arg == "--xps") {
+      if (options->output_format != OutputFormat::kNone) {
+        fprintf(stderr, "Duplicate or conflicting --xps argument\n");
+        return false;
+      }
+      options->output_format = OutputFormat::kXps;
+#endif  // _WIN32
 #endif  // PDF_ENABLE_SKIA
     } else if (ParseSwitchKeyValue(cur_arg, "--font-dir=", &value)) {
       if (!options->font_directory.empty()) {
@@ -764,71 +806,165 @@
   return true;
 }
 
-// Renderer for a single page.
-class PageRenderer {
+class Processor final {
  public:
-  virtual ~PageRenderer() = default;
+  Processor(const Options* options, const std::function<void()>* idler)
+      : options_(options), idler_(idler) {
+    DCHECK(options_);
+    DCHECK(idler_);
+  }
 
-  // Returns `true` if the rendered output exists. Must call `Finish()` first.
-  virtual bool HasOutput() const = 0;
+  const Options& options() const { return *options_; }
+  const std::function<void()>& idler() const { return *idler_; }
 
-  // Starts rendering the page, returning `false` on failure.
-  virtual bool Start() = 0;
+#ifdef _WIN32
+  ComFactory& com_factory() { return com_factory_; }
+#endif  // _WIN32
 
-  // Continues rendering the page, returning `false` when complete.
-  virtual bool Continue() { return false; }
+  // Invokes `idler()`.
+  void Idle() const { idler()(); }
 
-  // Finishes rendering the page.
-  virtual void Finish(FPDF_FORMHANDLE form) = 0;
-
-  // Writes rendered output to a file, returning `false` on failure.
-  virtual bool Write(const std::string& name, int page_index, bool md5) = 0;
-
- protected:
-  PageRenderer(FPDF_PAGE page, int width, int height, int flags)
-      : page_(page), width_(width), height_(height), flags_(flags) {}
-
-  FPDF_PAGE page() { return page_; }
-  int width() const { return width_; }
-  int height() const { return height_; }
-  int flags() const { return flags_; }
+  void ProcessPdf(const std::string& name,
+                  const char* buf,
+                  size_t len,
+                  const std::string& events);
 
  private:
-  FPDF_PAGE page_;
-  int width_;
-  int height_;
-  int flags_;
+  const Options* options_;
+  const std::function<void()>* idler_;
+
+#ifdef _WIN32
+  ComFactory com_factory_;
+#endif  // _WIN32
+};
+
+class PdfProcessor final {
+ public:
+  PdfProcessor(Processor* processor,
+               const std::string* name,
+               const std::string* events,
+               FPDF_DOCUMENT doc,
+               FPDF_FORMHANDLE form,
+               FPDF_FORMFILLINFO_PDFiumTest* form_fill_info)
+      : processor_(processor),
+        name_(name),
+        events_(events),
+        doc_(doc),
+        form_(form),
+        form_fill_info_(form_fill_info) {
+    DCHECK(processor_);
+    DCHECK(name_);
+    DCHECK(events_);
+    DCHECK(doc_);
+    DCHECK(form_);
+    DCHECK(form_fill_info_);
+  }
+
+  bool ProcessPage(int page_index);
+
+ private:
+  // Per processor state.
+  const Options& options() const { return processor_->options(); }
+  const std::function<void()>& idler() const { return processor_->idler(); }
+
+#ifdef _WIN32
+  ComFactory& com_factory() { return processor_->com_factory(); }
+#endif  // _WIN32
+
+  // Per PDF state.
+  const std::string& name() const { return *name_; }
+  const std::string& events() const { return *events_; }
+  FPDF_DOCUMENT doc() const { return doc_; }
+  FPDF_FORMHANDLE form() const { return form_; }
+
+  // Invokes `idler()`.
+  void Idle() const { idler()(); }
+
+  FPDF_PAGE GetPage(int page_index) const {
+    return GetPageForIndex(form_fill_info_, doc_, page_index);
+  }
+
+  Processor* processor_;
+  const std::string* name_;
+  const std::string* events_;
+  FPDF_DOCUMENT doc_;
+  FPDF_FORMHANDLE form_;
+  FPDF_FORMFILLINFO_PDFiumTest* form_fill_info_;
 };
 
 // Page renderer with bitmap output.
 class BitmapPageRenderer : public PageRenderer {
  public:
-  // Function type that writes a bitmap to an image file. The function returns
-  // the name of the image file on success, or an empty name on failure.
+  // Function type that writes rendered output to a file, returning `false` on
+  // failure.
   //
-  // Intended for use with some of the `pdfium_test_write_helper.h` functions.
-  using BitmapWriter = std::string (*)(const char* pdf_name,
-                                       int num,
-                                       void* buffer,
-                                       int stride,
-                                       int width,
-                                       int height);
+  // Intended to wrap functions from `pdfium_test_write_helper.h`.
+  using PageWriter = std::function<bool(BitmapPageRenderer& renderer,
+                                        const std::string& name,
+                                        int page_index,
+                                        bool md5)>;
+
+  // Wraps a `PageWriter` around a function pointer that writes the text page.
+  static PageWriter WrapPageWriter(
+      void (*text_page_writer)(FPDF_TEXTPAGE text_page,
+                               const char* pdf_name,
+                               int num)) {
+    return [text_page_writer](BitmapPageRenderer& renderer,
+                              const std::string& name, int page_index,
+                              bool /*md5*/) {
+      ScopedFPDFTextPage text_page(FPDFText_LoadPage(renderer.page()));
+      if (!text_page) {
+        return false;
+      }
+
+      text_page_writer(text_page.get(), name.c_str(), page_index);
+      return true;
+    };
+  }
+
+  // Wraps a `PageWriter` around a function pointer that writes the page.
+  static PageWriter WrapPageWriter(void (*page_writer)(FPDF_PAGE page,
+                                                       const char* pdf_name,
+                                                       int num)) {
+    return [page_writer](BitmapPageRenderer& renderer, const std::string& name,
+                         int page_index, bool /*md5*/) {
+      page_writer(renderer.page(), name.c_str(), page_index);
+      return true;
+    };
+  }
+
+  // Wraps a `PageWriter` around a function pointer that writes the rasterized
+  // bitmap to an image file.
+  static PageWriter WrapPageWriter(
+      std::string (*bitmap_writer)(const char* pdf_name,
+                                   int num,
+                                   void* buffer,
+                                   int stride,
+                                   int width,
+                                   int height)) {
+    return [bitmap_writer](BitmapPageRenderer& renderer,
+                           const std::string& name, int page_index, bool md5) {
+      int stride = FPDFBitmap_GetStride(renderer.bitmap());
+      void* buffer = FPDFBitmap_GetBuffer(renderer.bitmap());
+      std::string image_file_name = bitmap_writer(
+          name.c_str(), page_index, buffer, /*stride=*/stride,
+          /*width=*/renderer.width(), /*height=*/renderer.height());
+      if (image_file_name.empty()) {
+        return false;
+      }
+
+      if (md5) {
+        // Write the filename and the MD5 of the buffer to stdout.
+        OutputMD5Hash(image_file_name.c_str(),
+                      {static_cast<const uint8_t*>(buffer),
+                       static_cast<size_t>(stride) * renderer.height()});
+      }
+      return true;
+    };
+  }
 
   bool HasOutput() const override { return !!bitmap_; }
 
-  bool Start() override {
-    bool alpha = FPDFPage_HasTransparency(page());
-    bitmap_.reset(FPDFBitmap_Create(/*width=*/width(), /*height=*/height(),
-                                    /*alpha=*/alpha));
-    if (!bitmap_)
-      return false;
-
-    FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
-    FPDFBitmap_FillRect(bitmap(), /*left=*/0, /*top=*/0, /*width=*/width(),
-                        /*height=*/height(), /*color=*/fill_color);
-    return true;
-  }
-
   void Finish(FPDF_FORMHANDLE form) override {
     FPDF_FFLDraw(form, bitmap(), page(), /*start_x=*/0, /*start_y=*/0,
                  /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
@@ -837,24 +973,7 @@
   }
 
   bool Write(const std::string& name, int page_index, bool md5) override {
-    if (!writer_)
-      return false;
-
-    int stride = FPDFBitmap_GetStride(bitmap());
-    void* buffer = FPDFBitmap_GetBuffer(bitmap());
-    std::string image_file_name =
-        writer_(name.c_str(), /*num=*/page_index, buffer, /*stride=*/stride,
-                /*width=*/width(), /*height=*/height());
-    if (image_file_name.empty())
-      return false;
-
-    if (md5) {
-      // Write the filename and the MD5 of the buffer to stdout.
-      OutputMD5Hash(image_file_name.c_str(),
-                    {static_cast<const uint8_t*>(buffer),
-                     static_cast<size_t>(stride) * height()});
-    }
-    return true;
+    return writer_ && writer_(*this, name, page_index, md5);
   }
 
  protected:
@@ -863,17 +982,35 @@
                      int height,
                      int flags,
                      const std::function<void()>& idler,
-                     BitmapWriter writer)
+                     PageWriter writer)
       : PageRenderer(page, /*width=*/width, /*height=*/height, /*flags=*/flags),
         idler_(idler),
-        writer_(writer) {}
+        writer_(std::move(writer)) {}
+
+  bool InitializeBitmap(void* first_scan) {
+    bool alpha = FPDFPage_HasTransparency(page());
+    bitmap_.reset(FPDFBitmap_CreateEx(
+        /*width=*/width(), /*height=*/height(),
+        /*format=*/alpha ? FPDFBitmap_BGRA : FPDFBitmap_BGRx, first_scan,
+        /*stride=*/width() * sizeof(uint32_t)));
+    if (!bitmap()) {
+      return false;
+    }
+
+    FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
+    FPDFBitmap_FillRect(bitmap(), /*left=*/0, /*top=*/0, /*width=*/width(),
+                        /*height=*/height(), /*color=*/fill_color);
+    return true;
+  }
+
+  void ResetBitmap() { bitmap_.reset(); }
 
   void Idle() const { idler_(); }
   FPDF_BITMAP bitmap() { return bitmap_.get(); }
 
  private:
   const std::function<void()>& idler_;
-  BitmapWriter writer_;
+  PageWriter writer_;
   ScopedFPDFBitmap bitmap_;
 };
 
@@ -885,17 +1022,18 @@
                             int height,
                             int flags,
                             const std::function<void()>& idler,
-                            BitmapWriter writer)
+                            PageWriter writer)
       : BitmapPageRenderer(page,
                            /*width=*/width,
                            /*height=*/height,
                            /*flags=*/flags,
                            idler,
-                           writer) {}
+                           std::move(writer)) {}
 
   bool Start() override {
-    if (!BitmapPageRenderer::Start())
+    if (!InitializeBitmap(/*first_scan=*/nullptr)) {
       return false;
+    }
 
     // Note, client programs probably want to use this method instead of the
     // progressive calls. The progressive calls are if you need to pause the
@@ -915,22 +1053,23 @@
                                 int height,
                                 int flags,
                                 const std::function<void()>& idler,
-                                BitmapWriter writer,
+                                PageWriter writer,
                                 const FPDF_COLORSCHEME* color_scheme)
       : BitmapPageRenderer(page,
                            /*width=*/width,
                            /*height=*/height,
                            /*flags=*/flags,
                            idler,
-                           writer),
+                           std::move(writer)),
         color_scheme_(color_scheme) {
     pause_.version = 1;
     pause_.NeedToPauseNow = &NeedToPauseNow;
   }
 
   bool Start() override {
-    if (!BitmapPageRenderer::Start())
+    if (!InitializeBitmap(/*first_scan=*/nullptr)) {
       return false;
+    }
 
     if (FPDF_RenderPageBitmapWithColorScheme_Start(
             bitmap(), page(), /*start_x=*/0, /*start_y=*/0, /*size_x=*/width(),
@@ -961,28 +1100,159 @@
   bool to_be_continued_ = false;
 };
 
+#ifdef _WIN32
+class ScopedGdiDc final {
+ public:
+  ~ScopedGdiDc() { Reset(nullptr); }
+
+  void Reset(HDC dc) {
+    if (dc_) {
+      [[maybe_unused]] BOOL success = DeleteDC(dc_);
+      DCHECK(success);
+    }
+    dc_ = dc;
+  }
+
+  HDC Get() const { return dc_; }
+
+ private:
+  HDC dc_ = nullptr;
+};
+
+class ScopedGdiObject final {
+ public:
+  ~ScopedGdiObject() { Reset(nullptr); }
+
+  void Reset(HGDIOBJ object) {
+    if (object_) {
+      [[maybe_unused]] BOOL success = DeleteObject(object_);
+      DCHECK(success);
+    }
+    object_ = object;
+  }
+
+  HGDIOBJ Get() const { return object_; }
+
+ private:
+  HGDIOBJ object_ = nullptr;
+};
+
+class GdiDisplayPageRenderer : public BitmapPageRenderer {
+ public:
+  GdiDisplayPageRenderer(FPDF_PAGE page,
+                         int width,
+                         int height,
+                         int flags,
+                         const std::function<void()>& idler,
+                         PageWriter writer)
+      : BitmapPageRenderer(page,
+                           /*width=*/width,
+                           /*height=*/height,
+                           /*flags=*/flags,
+                           idler,
+                           std::move(writer)) {}
+
+  ~GdiDisplayPageRenderer() override {
+    // Need to free `bitmap()` first, in case it points at `dib_` memory.
+    ResetBitmap();
+  }
+
+  bool Start() override {
+    // Create an in-memory DC compatible with the display.
+    dc_.Reset(CreateCompatibleDC(/*hdc=*/nullptr));
+    if (!dc_.Get()) {
+      return false;
+    }
+
+    // Create a BGRA DIB and select it into the in-memory DC.
+    BITMAPINFO dib_info;
+    memset(&dib_info, 0, sizeof(BITMAPINFO));
+    dib_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    dib_info.bmiHeader.biWidth = width();
+    dib_info.bmiHeader.biHeight = -height();  // top-down
+    dib_info.bmiHeader.biPlanes = 1;
+    dib_info.bmiHeader.biBitCount = 32;
+    dib_info.bmiHeader.biCompression = BI_RGB;
+
+    VOID* dib_pixels;
+    dib_.Reset(CreateDIBSection(dc_.Get(), &dib_info, DIB_RGB_COLORS,
+                                &dib_pixels, /*hSection=*/nullptr,
+                                /*offset=*/0));
+    if (!dib_.Get() || !InitializeBitmap(dib_pixels)) {
+      return false;
+    }
+    pdfium::base::win::ScopedSelectObject select_dib(dc_.Get(), dib_.Get());
+
+    // Render into the in-memory DC.
+    FPDF_RenderPage(dc_.Get(), page(), /*start_x=*/0, /*start_y=*/0,
+                    /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0,
+                    /*flags=*/flags());
+
+    return !!GdiFlush();
+  }
+
+  void Finish(FPDF_FORMHANDLE /*form*/) override {
+    // Note that `fpdf_formfill.h` does not support GDI.
+
+    // The GDI backend doesn't support alpha and clears the alpha component to
+    // transparent, so clear the alpha component back to opaque.
+    const int stride = FPDFBitmap_GetStride(bitmap());
+    DCHECK_EQ(width() * sizeof(uint32_t), static_cast<size_t>(stride));
+    const int pixel_stride = stride / sizeof(uint32_t);
+
+    uint32_t* scanline =
+        reinterpret_cast<uint32_t*>(FPDFBitmap_GetBuffer(bitmap()));
+    for (int row = 0; row < height(); ++row) {
+      for (int column = 0; column < width(); ++column) {
+        scanline[column] |= 0xFF000000;
+      }
+      scanline += pixel_stride;
+    }
+  }
+
+ private:
+  ScopedGdiDc dc_;
+  ScopedGdiObject dib_;
+};
+#endif  // _WIN32
+
 #ifdef PDF_ENABLE_SKIA
-class SkPicturePageRenderer : public PageRenderer {
+class SkCanvasPageRenderer : public PageRenderer {
+ public:
+  bool Start() override {
+    FPDF_RenderPageSkia(reinterpret_cast<FPDF_SKIA_CANVAS>(canvas()), page(),
+                        width(), height());
+    return true;
+  }
+
+  void Finish(FPDF_FORMHANDLE form) override {
+    FPDF_FFLDrawSkia(form, reinterpret_cast<FPDF_SKIA_CANVAS>(canvas()), page(),
+                     /*start_x=*/0, /*start_y=*/0, width(), height(),
+                     /*rotate=*/0, flags());
+  }
+
+ protected:
+  SkCanvasPageRenderer(FPDF_PAGE page, int width, int height, int flags)
+      : PageRenderer(page, width, height, flags) {}
+
+  virtual SkCanvas* canvas() = 0;
+};
+
+class SkPicturePageRenderer final : public SkCanvasPageRenderer {
  public:
   SkPicturePageRenderer(FPDF_PAGE page, int width, int height, int flags)
-      : PageRenderer(page,
-                     /*width=*/width,
-                     /*height=*/height,
-                     /*flags=*/flags) {}
+      : SkCanvasPageRenderer(page, width, height, flags) {}
 
   bool HasOutput() const override { return !!picture_; }
 
   bool Start() override {
-    recorder_.reset(reinterpret_cast<SkPictureRecorder*>(
-        FPDF_RenderPageSkp(page(), /*size_x=*/width(), /*size_y=*/height())));
-    return !!recorder_;
+    recorder_ = std::make_unique<SkPictureRecorder>();
+    recorder_->beginRecording(width(), height());
+    return SkCanvasPageRenderer::Start();
   }
 
   void Finish(FPDF_FORMHANDLE form) override {
-    FPDF_FFLRecord(form, reinterpret_cast<FPDF_RECORDER>(recorder_.get()),
-                   page(), /*start_x=*/0, /*start_y=*/0, /*size_x=*/width(),
-                   /*size_y=*/height(), /*rotate=*/0, /*flags=*/0);
-
+    SkCanvasPageRenderer::Finish(form);
     picture_ = recorder_->finishRecordingAsPicture();
     recorder_.reset();
   }
@@ -994,8 +1264,8 @@
 
     if (md5) {
       // Play back the `SkPicture` so we can take a hash of the result.
-      sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(
-          /*width=*/width(), /*height=*/height());
+      sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(
+          /*width=*/width(), /*height=*/height()));
       if (!surface)
         return false;
 
@@ -1015,92 +1285,216 @@
     return true;
   }
 
+ protected:
+  SkCanvas* canvas() override { return recorder_->getRecordingCanvas(); }
+
  private:
   std::unique_ptr<SkPictureRecorder> recorder_;
   sk_sp<SkPicture> picture_;
 };
+
+class SkDocumentPageRenderer final : public SkCanvasPageRenderer {
+ public:
+  SkDocumentPageRenderer(std::unique_ptr<SkWStream> stream,
+                         sk_sp<SkDocument> document,
+                         FPDF_PAGE page,
+                         int width,
+                         int height,
+                         int flags)
+      : SkCanvasPageRenderer(page, width, height, flags),
+        stream_(std::move(stream)),
+        document_(std::move(document)) {
+    DCHECK(stream_);
+    DCHECK(document_);
+  }
+
+  bool HasOutput() const override { return has_output_; }
+
+  bool Start() override {
+    if (!document_) {
+      return false;
+    }
+
+    DCHECK(!canvas_);
+    canvas_ = document_->beginPage(width(), height());
+    if (!canvas_) {
+      return false;
+    }
+
+    return SkCanvasPageRenderer::Start();
+  }
+
+  void Finish(FPDF_FORMHANDLE form) override {
+    SkCanvasPageRenderer::Finish(form);
+
+    DCHECK(canvas_);
+    canvas_ = nullptr;
+    document_->endPage();
+
+    has_output_ = true;
+  }
+
+  bool Write(const std::string& /*name*/,
+             int /*page_index*/,
+             bool /*md5*/) override {
+    bool success = HasOutput();
+    if (success) {
+      document_->close();
+    } else {
+      document_->abort();
+    }
+
+    document_.reset();
+    stream_.reset();
+    return success;
+  }
+
+ protected:
+  SkCanvas* canvas() override { return canvas_; }
+
+ private:
+  std::unique_ptr<SkWStream> stream_;
+  sk_sp<SkDocument> document_;
+
+  SkCanvas* canvas_ = nullptr;
+  bool has_output_ = false;
+};
 #endif  // PDF_ENABLE_SKIA
 
-bool ProcessPage(const std::string& name,
-                 FPDF_DOCUMENT doc,
-                 FPDF_FORMHANDLE form,
-                 FPDF_FORMFILLINFO_PDFiumTest* form_fill_info,
-                 const int page_index,
-                 const Options& options,
-                 const std::string& events,
-                 const std::function<void()>& idler) {
-  FPDF_PAGE page = GetPageForIndex(form_fill_info, doc, page_index);
-  if (!page)
+bool PdfProcessor::ProcessPage(const int page_index) {
+  FPDF_PAGE page = GetPage(page_index);
+  if (!page) {
     return false;
-  if (options.send_events)
-    SendPageEvents(form, page, events, idler);
-  if (options.save_images)
-    WriteImages(page, name.c_str(), page_index);
-  if (options.save_rendered_images)
-    WriteRenderedImages(doc, page, name.c_str(), page_index);
-  if (options.save_thumbnails)
-    WriteThumbnail(page, name.c_str(), page_index);
-  if (options.save_thumbnails_decoded)
-    WriteDecodedThumbnailStream(page, name.c_str(), page_index);
-  if (options.save_thumbnails_raw)
-    WriteRawThumbnailStream(page, name.c_str(), page_index);
-  if (options.output_format == OutputFormat::kPageInfo) {
+  }
+
+  if (options().send_events) {
+    SendPageEvents(form(), page, events(), idler());
+  }
+  if (options().save_images) {
+    WriteImages(page, name().c_str(), page_index);
+  }
+  if (options().save_rendered_images) {
+    WriteRenderedImages(doc(), page, name().c_str(), page_index);
+  }
+  if (options().save_thumbnails) {
+    WriteThumbnail(page, name().c_str(), page_index);
+  }
+  if (options().save_thumbnails_decoded) {
+    WriteDecodedThumbnailStream(page, name().c_str(), page_index);
+  }
+  if (options().save_thumbnails_raw) {
+    WriteRawThumbnailStream(page, name().c_str(), page_index);
+  }
+  if (options().output_format == OutputFormat::kPageInfo) {
     DumpPageInfo(page, page_index);
     return true;
   }
-  if (options.output_format == OutputFormat::kStructure) {
+  if (options().output_format == OutputFormat::kStructure) {
     DumpPageStructure(page, page_index);
     return true;
   }
 
   ScopedFPDFTextPage text_page(FPDFText_LoadPage(page));
   double scale = 1.0;
-  if (!options.scale_factor_as_string.empty())
-    std::stringstream(options.scale_factor_as_string) >> scale;
+  if (!options().scale_factor_as_string.empty()) {
+    std::stringstream(options().scale_factor_as_string) >> scale;
+  }
 
   int width = static_cast<int>(FPDF_GetPageWidthF(page) * scale);
   int height = static_cast<int>(FPDF_GetPageHeightF(page) * scale);
-  int flags = PageRenderFlagsFromOptions(options);
+  int flags = PageRenderFlagsFromOptions(options());
 
   std::unique_ptr<PageRenderer> renderer;
-#ifdef PDF_ENABLE_SKIA
-  if (options.output_format == OutputFormat::kSkp) {
-    renderer = std::make_unique<SkPicturePageRenderer>(
-        page, /*width=*/width, /*height=*/height, /*flags=*/flags);
-  } else {
-#else
-  {
-#endif  // PDF_ENABLE_SKIA
-    BitmapPageRenderer::BitmapWriter writer;
-    switch (options.output_format) {
+  BitmapPageRenderer::PageWriter writer;
+  switch (options().output_format) {
+    case OutputFormat::kText:
+      writer = BitmapPageRenderer::WrapPageWriter(WriteText);
+      break;
+
+    case OutputFormat::kAnnot:
+      writer = BitmapPageRenderer::WrapPageWriter(WriteAnnot);
+      break;
+
+    case OutputFormat::kPpm:
+      writer = BitmapPageRenderer::WrapPageWriter(WritePpm);
+      break;
+
+    case OutputFormat::kPng:
+      writer = BitmapPageRenderer::WrapPageWriter(WritePng);
+      break;
+
 #ifdef _WIN32
-      case OutputFormat::kBmp:
-        writer = WriteBmp;
-        break;
+    case OutputFormat::kBmp:
+      writer = BitmapPageRenderer::WrapPageWriter(WriteBmp);
+      break;
+
+    case OutputFormat::kEmf:
+      // TODO(crbug.com/pdfium/2054): Render directly to DC.
+      writer = BitmapPageRenderer::WrapPageWriter(WriteEmf);
+      break;
+
+    case OutputFormat::kPs2:
+    case OutputFormat::kPs3:
+      // TODO(crbug.com/pdfium/2054): Render directly to DC.
+      writer = BitmapPageRenderer::WrapPageWriter(WritePS);
+      break;
 #endif  // _WIN32
 
-      case OutputFormat::kPng:
-        writer = WritePng;
-        break;
+#ifdef PDF_ENABLE_SKIA
+    case OutputFormat::kSkp:
+      renderer = std::make_unique<SkPicturePageRenderer>(
+          page, /*width=*/width, /*height=*/height, /*flags=*/flags);
+      break;
 
-      case OutputFormat::kPpm:
-        writer = WritePpm;
+#ifdef _WIN32
+    case OutputFormat::kXps: {
+      IXpsOMObjectFactory* xps_factory = com_factory().GetXpsOMObjectFactory();
+      if (!xps_factory) {
         break;
+      }
 
-      default:
-        // Other formats won't write the output to a file, but still rasterize.
-        writer = nullptr;
+      std::unique_ptr<SkWStream> stream =
+          WriteToSkWStream(name(), page_index, "xps");
+      if (!stream) {
         break;
+      }
+
+      sk_sp<SkDocument> document =
+          SkXPS::MakeDocument(stream.get(), xps_factory);
+      if (!document) {
+        break;
+      }
+
+      renderer = std::make_unique<SkDocumentPageRenderer>(
+          std::move(stream), std::move(document), page, width, height, flags);
+      break;
     }
+#endif  // _WIN32
+#endif  // PDF_ENABLE_SKIA
 
-    if (options.render_oneshot) {
+    default:
+      // Other formats won't write the output to a file, but still rasterize.
+      break;
+  }
+
+#ifdef _WIN32
+  if (!renderer && options().use_renderer_type == RendererType::kGdi) {
+    renderer = std::make_unique<GdiDisplayPageRenderer>(
+        page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler(),
+        std::move(writer));
+  }
+#endif  // _WIN32
+
+  if (!renderer) {
+    // Use a rasterizing page renderer by default.
+    if (options().render_oneshot) {
       renderer = std::make_unique<OneShotBitmapPageRenderer>(
-          page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler,
-          writer);
+          page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler(),
+          std::move(writer));
     } else {
       // Client programs will be setting these values when rendering.
       // This is a sample color scheme with distinct colors.
-      // Used only when `options.forced_color` is true.
+      // Used only when `options().forced_color` is true.
       FPDF_COLORSCHEME color_scheme;
       color_scheme.path_fill_color = 0xFFFF0000;
       color_scheme.path_stroke_color = 0xFF00FF00;
@@ -1108,59 +1502,33 @@
       color_scheme.text_stroke_color = 0xFF00FFFF;
 
       renderer = std::make_unique<ProgressiveBitmapPageRenderer>(
-          page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler,
-          writer, options.forced_color ? &color_scheme : nullptr);
+          page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler(),
+          std::move(writer), options().forced_color ? &color_scheme : nullptr);
     }
   }
 
   if (renderer->Start()) {
     while (renderer->Continue())
       continue;
-    renderer->Finish(form);
-
-    switch (options.output_format) {
-#ifdef _WIN32
-      case OutputFormat::kEmf:
-        WriteEmf(page, name.c_str(), page_index);
-        break;
-
-      case OutputFormat::kPs2:
-      case OutputFormat::kPs3:
-        WritePS(page, name.c_str(), page_index);
-        break;
-#endif  // _WIN32
-
-      case OutputFormat::kText:
-        WriteText(text_page.get(), name.c_str(), page_index);
-        break;
-
-      case OutputFormat::kAnnot:
-        WriteAnnot(page, name.c_str(), page_index);
-        break;
-
-      default:
-        renderer->Write(name, page_index, /*md5=*/options.md5);
-        break;
-    }
+    renderer->Finish(form());
+    renderer->Write(name(), page_index, /*md5=*/options().md5);
   } else {
     fprintf(stderr, "Page was too large to be rendered.\n");
   }
 
-  FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
-  idler();
+  FORM_DoPageAAction(page, form(), FPDFPAGE_AACTION_CLOSE);
+  Idle();
 
-  FORM_OnBeforeClosePage(page, form);
-  idler();
+  FORM_OnBeforeClosePage(page, form());
+  Idle();
 
   return renderer->HasOutput();
 }
 
-void ProcessPdf(const std::string& name,
-                const char* buf,
-                size_t len,
-                const Options& options,
-                const std::string& events,
-                const std::function<void()>& idler) {
+void Processor::ProcessPdf(const std::string& name,
+                           const char* buf,
+                           size_t len,
+                           const std::string& events) {
   TestLoader loader({buf, len});
 
   FPDF_FILEACCESS file_access = {};
@@ -1183,9 +1551,9 @@
   ScopedFPDFDocument doc;
 
   const char* password =
-      options.password.empty() ? nullptr : options.password.c_str();
+      options().password.empty() ? nullptr : options().password.c_str();
   bool is_linearized = false;
-  if (options.use_load_mem_document) {
+  if (options().use_load_mem_document) {
     doc.reset(FPDF_LoadMemDocument(buf, len, password));
   } else {
     if (FPDFAvail_IsLinearized(pdf_avail.get()) == PDF_LINEARIZED) {
@@ -1224,11 +1592,13 @@
 
   (void)FPDF_GetDocPermissions(doc.get());
 
-  if (options.show_metadata)
+  if (options().show_metadata) {
     DumpMetaData(doc.get());
+  }
 
-  if (options.save_attachments)
+  if (options().save_attachments) {
     WriteAttachments(doc.get(), name);
+  }
 
 #ifdef PDF_ENABLE_V8
   IPDF_JSPLATFORM platform_callbacks = {};
@@ -1248,7 +1618,7 @@
 #ifdef PDF_ENABLE_XFA
   form_callbacks.version = 2;
   form_callbacks.xfa_disabled =
-      options.disable_xfa || options.disable_javascript;
+      options().disable_xfa || options().disable_javascript;
   form_callbacks.FFI_PopupMenu = ExamplePopupMenu;
 #else   // PDF_ENABLE_XFA
   form_callbacks.version = 1;
@@ -1257,8 +1627,9 @@
   form_callbacks.FFI_GetPage = GetPageForIndex;
 
 #ifdef PDF_ENABLE_V8
-  if (!options.disable_javascript)
+  if (!options().disable_javascript) {
     form_callbacks.m_pJsPlatform = &platform_callbacks;
+  }
 #endif  // PDF_ENABLE_V8
 
   ScopedFPDFFormHandle form(
@@ -1266,7 +1637,7 @@
   form_callbacks.form_handle = form.get();
 
 #ifdef PDF_ENABLE_XFA
-  if (!options.disable_xfa && !options.disable_javascript) {
+  if (!options().disable_xfa && !options().disable_javascript) {
     int doc_type = FPDF_GetFormType(doc.get());
     if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) {
       if (!FPDF_LoadXFA(doc.get()))
@@ -1281,19 +1652,22 @@
   FORM_DoDocumentOpenAction(form.get());
 
 #if _WIN32
-  if (options.output_format == OutputFormat::kPs2)
+  if (options().output_format == OutputFormat::kPs2) {
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
-  else if (options.output_format == OutputFormat::kPs3)
+  } else if (options().output_format == OutputFormat::kPs3) {
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
-  else if (options.output_format == OutputFormat::kPs3Type42)
+  } else if (options().output_format == OutputFormat::kPs3Type42) {
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3_TYPE42);
+  }
 #endif
 
   int page_count = FPDF_GetPageCount(doc.get());
   int processed_pages = 0;
   int bad_pages = 0;
-  int first_page = options.pages ? options.first_page : 0;
-  int last_page = options.pages ? options.last_page + 1 : page_count;
+  int first_page = options().pages ? options().first_page : 0;
+  int last_page = options().pages ? options().last_page + 1 : page_count;
+  PdfProcessor pdf_processor(this, &name, &events, doc.get(), form.get(),
+                             &form_callbacks);
   for (int i = first_page; i < last_page; ++i) {
     if (is_linearized) {
       int avail_status = PDF_DATA_NOTAVAIL;
@@ -1306,17 +1680,16 @@
         return;
       }
     }
-    if (ProcessPage(name, doc.get(), form.get(), &form_callbacks, i, options,
-                    events, idler)) {
+    if (pdf_processor.ProcessPage(i)) {
       ++processed_pages;
     } else {
       ++bad_pages;
     }
-    idler();
+    Idle();
   }
 
   FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC);
-  idler();
+  Idle();
 
   fprintf(stderr, "Processed %d pages.\n", processed_pages);
   if (bad_pages)
@@ -1346,6 +1719,9 @@
 #ifdef PDF_ENABLE_SKIA
   append_config("SKIA");
 #endif
+#ifdef _WIN32
+  append_config("GDI");
+#endif
   printf("%s\n", config.c_str());
 }
 
@@ -1385,9 +1761,21 @@
     "<pdf-name>.thumbnail.decoded.<page-number>.png\n"
     "  --save-thumbs-raw      - write page thumbnails' raw stream data"
     "<pdf-name>.thumbnail.raw.<page-number>.png\n"
-#if defined(_SKIA_SUPPORT_)
+
+#if defined(PDF_ENABLE_SKIA)
+#ifdef _WIN32
+    "  --use-renderer         - renderer to use, one of [agg | gdi | skia]\n"
+#else
     "  --use-renderer         - renderer to use, one of [agg | skia]\n"
-#endif
+#endif  // _WIN32
+#else
+#ifdef _WIN32
+    "  --use-renderer         - renderer to use, one of [agg | gdi]\n"
+#else
+    "  --use-renderer         - renderer to use, one of [agg]\n"
+#endif  // _WIN32
+#endif  // defined(PDF_ENABLE_SKIA)
+
 #ifdef PDF_ENABLE_V8
     "  --disable-javascript   - do not execute JS in PDF files\n"
     "  --js-flags=<flags>     - additional flags to pass to V8\n"
@@ -1424,7 +1812,10 @@
     "  --annot - write annotation info <pdf-name>.<page-number>.annot.txt\n"
 #ifdef PDF_ENABLE_SKIA
     "  --skp   - write page images <pdf-name>.<page-number>.skp\n"
-#endif
+#ifdef _WIN32
+    "  --xps   - write page images <pdf-name>.<page-number>.xps\n"
+#endif  // _WIN32
+#endif  // PDF_ENABLE_SKIA
     "  --md5   - write output image paths and their md5 hashes to stdout.\n"
     "  --time=<number> - Seconds since the epoch to set system time.\n"
     "";
@@ -1445,6 +1836,10 @@
 }  // namespace
 
 int main(int argc, const char* argv[]) {
+#if defined(PDF_USE_PARTITION_ALLOC)
+  pdfium::ConfigurePartitionAllocShimPartitionForTest();
+#endif
+
   SetUpErrorHandling();
   setlocale(LC_CTYPE, "en_US.UTF-8");  // For printf() of high-characters.
 
@@ -1466,23 +1861,40 @@
     return 1;
   }
 
-  const FPDF_RENDERER_TYPE renderer_type =
-      options.use_renderer_type.value_or(GetDefaultRendererType());
-#if defined(PDF_ENABLE_SKIA) && defined(BUILD_WITH_CHROMIUM)
-  if (renderer_type == FPDF_RENDERERTYPE_SKIA) {
-    // Needed to support Chromium's copy of Skia, which uses a
-    // DiscardableMemoryAllocator.
-    chromium_support::InitializeDiscardableMemoryAllocator();
-  }
-#endif
-
   FPDF_LIBRARY_CONFIG config;
   config.version = 4;
   config.m_pUserFontPaths = nullptr;
   config.m_pIsolate = nullptr;
   config.m_v8EmbedderSlot = 0;
   config.m_pPlatform = nullptr;
-  config.m_RendererType = renderer_type;
+
+  switch (options.use_renderer_type) {
+    case RendererType::kDefault:
+      config.m_RendererType = GetDefaultRendererType();
+      break;
+
+    case RendererType::kAgg:
+      config.m_RendererType = FPDF_RENDERERTYPE_AGG;
+      break;
+
+#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)
+    case RendererType::kSkia:
+#if defined(BUILD_WITH_CHROMIUM)
+      // Needed to support Chromium's copy of Skia, which uses a
+      // `DiscardableMemoryAllocator`.
+      chromium_support::InitializeDiscardableMemoryAllocator();
+#endif  // defined(BUILD_WITH_CHROMIUM)
+      config.m_RendererType = FPDF_RENDERERTYPE_SKIA;
+      break;
+#endif  // defined(PDF_ENABLE_SKIA)
+  }
 
   std::function<void()> idler = []() {};
 #ifdef PDF_ENABLE_V8
@@ -1547,6 +1959,7 @@
     FSDK_SetLocaltimeFunction([](const time_t* tp) { return gmtime(tp); });
   }
 
+  Processor processor(&options, &idler);
   for (const std::string& filename : files) {
     size_t file_length = 0;
     std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
@@ -1580,8 +1993,7 @@
       }
     }
 
-    ProcessPdf(filename, file_contents.get(), file_length, options, events,
-               idler);
+    processor.ProcessPdf(filename, file_contents.get(), file_length, events);
 
 #ifdef ENABLE_CALLGRIND
     if (options.callgrind_delimiters)
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index ff91366..f9d1bf8 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -173,7 +173,9 @@
   sources += skia_core_public
   sources += skia_sksl_gpu_sources
   sources += skia_sksl_sources
-  sources += skia_utils_sources
+  sources += skia_utils_private
+  sources += skia_xps_sources
+  sources += skia_needs_sksl_sources
   sources += skia_codec_core
   sources += skia_codec_decode_bmp
   sources += skia_encode_srcs
@@ -213,13 +215,15 @@
     "//third_party/skia/src/utils/SkParsePath.cpp",
   ]
 
+  if (is_win) {
+    libs = [ "fontsub.lib" ]
+  }
+
   # need separate win section to handle chromes auto gn filter
   # (build/config/BUILDCONFIG.gn)
   if (is_win) {
     sources -= [
       #windows
-      "//third_party/skia/src/utils/win/SkAutoCoInitialize.cpp",
-      "//third_party/skia/src/utils/win/SkIStream.cpp",
       "//third_party/skia/src/utils/win/SkWGL_win.cpp",
     ]
   }
@@ -350,7 +354,7 @@
 
   if (is_android) {
     deps += [
-      "//third_party/android_ndk:cpu_features",
+      "//third_party/cpu_features:ndk_compat",
       "//third_party/expat",
     ]
   }
@@ -409,19 +413,14 @@
   defines = []
   sources = skia_core_sources
   sources += skia_effects_sources
+  sources += skia_colorfilters_sources
+  sources += skia_colorfilters_sksl_sources
   sources += skia_effects_imagefilter_sources
 
   visibility = [ ":skia" ]
 }
 
 # Bits that involve special vector-y hardware.
-if (current_cpu == "arm64") {
-  skia_source_set("skia_opts_crc32") {
-    sources = skia_opts.crc32_sources
-    cflags = [ "-march=armv8-a+crc" ]
-    visibility = [ ":skia_opts" ]
-  }
-}
 if (current_cpu == "x86" || current_cpu == "x64") {
   skia_source_set("skia_opts_sse3") {
     sources = skia_opts.ssse3_sources
@@ -433,16 +432,6 @@
     }
     visibility = [ ":skia_opts" ]
   }
-  skia_source_set("skia_opts_sse42") {
-    sources = skia_opts.sse42_sources
-    if (!is_win || is_clang) {
-      cflags = [ "-msse4.2" ]
-    }
-    if (is_win) {
-      defines = [ "SK_CPU_SSE_LEVEL=42" ]
-    }
-    visibility = [ ":skia_opts" ]
-  }
   skia_source_set("skia_opts_avx") {
     sources = skia_opts.avx_sources
     if (!is_win) {
@@ -469,16 +458,6 @@
     }
     visibility = [ ":skia_opts" ]
   }
-  skia_source_set("skia_opts_skx") {
-    sources = skia_opts.skx_sources
-    if (!is_win) {
-      cflags = [ "-march=skylake-avx512" ]
-    }
-    if (is_win) {
-      cflags = [ "/arch:AVX512" ]
-    }
-    visibility = [ ":skia_opts" ]
-  }
 }
 
 skia_source_set("skia_opts") {
@@ -489,9 +468,7 @@
     deps = [
       ":skia_opts_avx",
       ":skia_opts_hsw",
-      ":skia_opts_skx",
       ":skia_opts_sse3",
-      ":skia_opts_sse42",
     ]
   } else if (current_cpu == "arm") {
     # The assembly uses the frame pointer register (r7 in Thumb/r11 in
@@ -509,7 +486,7 @@
       }
     }
   } else if (current_cpu == "arm64") {
-    deps = [ ":skia_opts_crc32" ]
+    # Conditional and empty body needed to avoid assert below
   } else if (current_cpu == "mipsel" || current_cpu == "mips64el") {
     cflags += [ "-fomit-frame-pointer" ]
   } else {
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 034de3c..331c2af 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build_overrides/build.gni")
 import("../pdfium.gni")
 
 source_set("test_support") {
@@ -49,6 +51,13 @@
     "../:pdfium_noshorten_config",
   ]
   visibility = [ "../*" ]
+  if (pdf_use_partition_alloc) {
+    sources += [
+      "allocator_shim_config.cpp",
+      "allocator_shim_config.h",
+    ]
+    deps += [ "//base/allocator/partition_allocator:partition_alloc" ]
+  }
   if (pdf_enable_v8) {
     sources += [
       "v8_initializer.cpp",
@@ -170,7 +179,6 @@
   deps = [
     "../:pdfium_public_headers",
     "../core/fdrm",
-    "../core/fxcrt",
     "../core/fxge",
     "../third_party:pdfium_base",
     "//testing/gmock",
@@ -179,6 +187,7 @@
   public_deps = [
     ":test_environments",
     ":test_support",
+    "../core/fxcrt",
   ]
   configs += [
     "../:pdfium_strict_config",
diff --git a/testing/DEPS b/testing/DEPS
index d0dc172..d0dd4fb 100644
--- a/testing/DEPS
+++ b/testing/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  '+base',
   '+core',
   '+fpdfsdk',
   '+fxjs',
diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS
index b50d3ce..47b807c 100644
--- a/testing/SUPPRESSIONS
+++ b/testing/SUPPRESSIONS
@@ -10,7 +10,7 @@
 # Column 1: platform: *, win, mac, linux
 # Column 2: v8 support: *, nov8, v8
 # Column 3: xfa support: *, noxfa, xfa
-# Column 4: rendering support: *, agg, skia
+# Column 4: rendering support: *, agg, gdi, skia
 #
 # All columns on a line on a line must match, but filenames may be repeated
 # on subsequent lines to suppress more cases.  Within each column, any one of
@@ -313,6 +313,300 @@
 Sum.pdf * * * *
 Test_CheckBox.pdf * * * *
 
+# GDI
+
+# TODO(pdfium:2054): Remove after associated bug is fixed
+1.pdf * * * gdi
+123.pdf * * * gdi
+1_image.pdf * * * gdi
+2_halftone.pdf * * * gdi
+3_alternate.pdf * * * gdi
+3_interpolate_image.pdf * * * gdi
+3bigpreview.pdf * * * gdi
+4_3.pdf * * * gdi
+4_36.pdf * * * gdi
+FRC_10_8.2.4_View_C.pdf * * * gdi
+FRC_8.4.1_Annotations_Type.pdf * * * gdi
+FRC_8.4.3_Border_Stypes_W_different_values.pdf * * * gdi
+FRC_8.5_Screen_Img_D_Launch.pdf * * * gdi
+FRC_8.5_URI_IsMap.pdf * * * gdi
+bug_691967.pdf * * * gdi
+bug_86459.pdf * * * gdi
+bug_880920.pdf * * * gdi
+bug_898443.pdf * * * gdi
+ch_7_0.pdf * * * gdi
+ch_7_1.pdf * * * gdi
+ch_7_4.pdf * * * gdi
+ch_7_5.pdf * * * gdi
+ch_7_6.pdf * * * gdi
+ch_7_7.pdf * * * gdi
+ch_7_8.pdf * * * gdi
+en_tem.pdf * * * gdi
+example_001.pdf * * * gdi
+example_003.pdf * * * gdi
+example_004.pdf * * * gdi
+example_005.pdf * * * gdi
+example_006.pdf * * * gdi
+example_007.pdf * * * gdi
+example_008.pdf * * * gdi
+example_009.pdf * * * gdi
+example_010.pdf * * * gdi
+example_011.pdf * * * gdi
+example_013.pdf * * * gdi
+example_015.pdf * * * gdi
+example_016.pdf * * * gdi
+example_017.pdf * * * gdi
+example_018.pdf * * * gdi
+example_019.pdf * * * gdi
+example_020.pdf * * * gdi
+example_021.pdf * * * gdi
+example_022.pdf * * * gdi
+example_023.pdf * * * gdi
+example_024.pdf * * * gdi
+example_025.pdf * * * gdi
+example_026.pdf * * * gdi
+example_027.pdf * * * gdi
+example_029.pdf * * * gdi
+example_030.pdf * * * gdi
+example_031.pdf * * * gdi
+example_032.pdf * * * gdi
+example_033.pdf * * * gdi
+example_034.pdf * * * gdi
+example_035.pdf * * * gdi
+example_036.pdf * * * gdi
+example_037.pdf * * * gdi
+example_038.pdf * * * gdi
+example_039.pdf * * * gdi
+example_040.pdf * * * gdi
+example_041.pdf * * * gdi
+example_042.pdf * * * gdi
+example_043.pdf * * * gdi
+example_044.pdf * * * gdi
+example_045.pdf * * * gdi
+example_046.pdf * * * gdi
+example_047.pdf * * * gdi
+example_048.pdf * * * gdi
+example_049.pdf * * * gdi
+example_050.pdf * * * gdi
+example_051.pdf * * * gdi
+example_052.pdf * * * gdi
+example_053.pdf * * * gdi
+example_055.pdf * * * gdi
+example_056.pdf * * * gdi
+example_057.pdf * * * gdi
+example_058.pdf * * * gdi
+example_059.pdf * * * gdi
+example_060.pdf * * * gdi
+example_061.pdf * * * gdi
+example_062.pdf * * * gdi
+example_063.pdf * * * gdi
+example_064.pdf * * * gdi
+example_065.pdf * * * gdi
+image_8bit_devicergb_dctdecode.pdf * * * gdi
+image_bmp.pdf * * * gdi
+image_foxit.pdf * * * gdi
+image_jpg.pdf * * * gdi
+image_png.pdf * * * gdi
+image_tif.pdf * * * gdi
+lines.pdf * * * gdi
+mask_array.pdf * * * gdi
+new_stamp2.pdf * * * gdi
+quick_start_guide.pdf * * * gdi
+whats_new_in_v3.0.pdf * * * gdi
+xfermodeimagefilter.pdf * * * gdi
+
+# TODO(pdfium:2055): Remove after associated bug is fixed
+Date_FormCale.pdf * * * gdi
+FRC_3.5_P__3648_Password_1.pdf * * * gdi
+FRC_8.4.1_Annotations_AP_N_R.D_.pdf * * * gdi
+FRC_8.4.1_Annotations_AS_Off_.pdf * * * gdi
+FRC_8.4.1_Annotations_AS_Yes_.pdf * * * gdi
+FRC_8.5_Bl_Hide.pdf * * * gdi
+FRC_8.5_E_GoTo_D.pdf * * * gdi
+FRC_8.5_Fo_URI_Base.pdf * * * gdi
+FRC_8.5_U_GoToR_NewWindow.pdf * * * gdi
+FRC_8.5_U_GoToR_NewWindow_2.pdf * * * gdi
+FRC_8.5_Widget_C.pdf * * * gdi
+FRC_8.5_Widget_F.pdf * * * gdi
+FRC_8.5_Widget_K.pdf * * * gdi
+FRC_8.5_Widget_V.pdf * * * gdi
+FRC_8.5_X_GoToR_D.pdf * * * gdi
+Line_Stroke.pdf * * * gdi
+Oneof1.pdf * * * gdi
+Oneof2.pdf * * * gdi
+PagePosition_any_rest.pdf * * * gdi
+PagePosition_first.pdf * * * gdi
+PagePosition_last.pdf * * * gdi
+PagePosition_no_any.pdf * * * gdi
+PagePosition_rest.pdf * * * gdi
+PagePosition_rest_any.pdf * * * gdi
+Test_DateField_locale_en_CA.pdf * * * gdi
+Test_DateField_locale_en_GB.pdf * * * gdi
+Test_DateField_locale_en_US.pdf * * * gdi
+Test_DateField_locale_fr_CA.pdf * * * gdi
+Test_DateField_locale_fr_FR.pdf * * * gdi
+Test_DateField_locale_nl_NL.pdf * * * gdi
+Test_DateField_locale_zh_CN.pdf * * * gdi
+Test_DateField_locale_zh_HK.pdf * * * gdi
+Test_Drop_downList.pdf * * * gdi
+Test_NumericField.pdf * * * gdi
+Test_PasswordField.pdf * * * gdi
+Test_RadioButton.pdf * * * gdi
+Test_ResetButton.pdf * * * gdi
+Test_TextField.pdf * * * gdi
+TimeField.pdf * * * gdi
+action_execute_a_menu_item.pdf * * * gdi
+action_hide_show_form.pdf * * * gdi
+action_on_focus.pdf * * * gdi
+action_reset.pdf * * * gdi
+action_run_javascript.pdf * * * gdi
+action_submit_a_form.pdf * * * gdi
+all_trigger_browsefordoc.pdf * * * gdi
+all_trigger_newdoc.pdf * * * gdi
+all_trigger_run_js_lunchurl.pdf * * * gdi
+all_trigger_run_js_maildoc.pdf * * * gdi
+annotation_highlight_author_content.pdf * * * gdi
+annotation_highlight_long_content.pdf * * * gdi
+annotation_highlight_no_author.pdf * * * gdi
+app_launchurl.pdf * * * gdi
+bi.pdf * * * gdi
+bug_434.pdf * * * gdi
+bug_440132.pdf * * * gdi
+calculate_average.pdf * * * gdi
+calculate_sum_a_b_c.pdf * * * gdi
+calculate_validate.pdf * * * gdi
+check_box_no_color.pdf * * * gdi
+combo_box_format.pdf * * * gdi
+date.pdf * * * gdi
+event.change.pdf * * * gdi
+event.changeex.pdf * * * gdi
+event.keydown.pdf * * * gdi
+event.keydown_1_.pdf * * * gdi
+event.shift.pdf * * * gdi
+event.type_name.pdf * * * gdi
+event.value.pdf * * * gdi
+event_change.pdf * * * gdi
+event_fieldfull.pdf * * * gdi
+event_fieldfull_1_.pdf * * * gdi
+example_014.pdf * * * gdi
+example_054.pdf * * * gdi
+form_button0.pdf * * * gdi
+form_button1.pdf * * * gdi
+form_button10.pdf * * * gdi
+form_button2.pdf * * * gdi
+form_button3.pdf * * * gdi
+form_button4.pdf * * * gdi
+form_button5.pdf * * * gdi
+form_button7.pdf * * * gdi
+form_button8.pdf * * * gdi
+form_button9.pdf * * * gdi
+form_checkbox.pdf * * * gdi
+form_checkbox1.pdf * * * gdi
+form_checkbox2.pdf * * * gdi
+form_checkbox3.pdf * * * gdi
+form_combobox_date.pdf * * * gdi
+form_combobox_date2.pdf * * * gdi
+form_combobox_num.pdf * * * gdi
+form_combobox_per.pdf * * * gdi
+form_combobox_plus.pdf * * * gdi
+form_combobox_product.pdf * * * gdi
+form_combobox_time.pdf * * * gdi
+form_radio.pdf * * * gdi
+form_same.pdf * * * gdi
+format_alert_box.pdf * * * gdi
+format_combo_box.pdf * * * gdi
+format_custom_format.pdf * * * gdi
+format_date.pdf * * * gdi
+format_number.pdf * * * gdi
+format_percentage.pdf * * * gdi
+format_special.pdf * * * gdi
+format_text_color.pdf * * * gdi
+getarray.pdf * * * gdi
+js_value_chack_box.pdf * * * gdi
+negative.pdf * * * gdi
+new_certify1.pdf * * * gdi
+new_signature1.pdf * * * gdi
+new_signature2.pdf * * * gdi
+none.pdf * * * gdi
+open_a_weblink.pdf * * * gdi
+percentage.pdf * * * gdi
+rotate.pdf * * * gdi
+show_1.pdf * * * gdi
+simplified_field_notation.pdf * * * gdi
+special.pdf * * * gdi
+submit_form.pdf * * * gdi
+text_field_font_input_decimal_point.pdf * * * gdi
+text_field_multiline_line_spacing.pdf * * * gdi
+time.pdf * * * gdi
+validate_alert_box.pdf * * * gdi
+widget_javascript.pdf * * * gdi
+
+# TODO(pdfium:2056): Remove after associated bug is fixed
+11.pdf * * * gdi
+1_10_watermark.pdf * * * gdi
+1_matrix.pdf * * * gdi
+2_color_tiling.pdf * * * gdi
+2_shading_type_6_00.pdf * * * gdi
+2_shading_type_6_001.pdf * * * gdi
+2_uncolor_tiling.pdf * * * gdi
+3_image_imagemask.pdf * * * gdi
+FRC_11_8.2.4_View_edit.pdf * * * gdi
+FRC_12_8.2.4_View_remove_all.pdf * * * gdi
+FRC_13_8.2.4_View_remove_value.pdf * * * gdi
+FRC_14_8.2.4_Sort_remove_all.pdf * * * gdi
+FRC_15_8.2.4_Sort_remove_value.pdf * * * gdi
+FRC_1_8.2.4_Type_8.6_.pdf * * * gdi
+FRC_2_8.2.4_Type_8.6__remove_value.pdf * * * gdi
+FRC_3.5_AuthEvent_EFOpen.pdf * * * gdi
+FRC_3.5_CFM_AESV2__EncryptMetadata_F.pdf * * * gdi
+FRC_3.5_CF_EFF_StdCF_Strf_Stmf_Identity.pdf * * * gdi
+FRC_3.5_CF_Strf_stmf_StdCF.pdf * * * gdi
+FRC_3.5_EncryptMetadata_None.pdf * * * gdi
+FRC_3.5_V_4_CFM_V2_.pdf * * * gdi
+FRC_3.5_V_5_CFM_AESV3.pdf * * * gdi
+FRC_3.5_v_1_length_40_Filter_standard.pdf * * * gdi
+FRC_3.5_v_2_length_128_AuthEvent_DocOpen_.pdf * * * gdi
+FRC_3_8.2.4_Type_8.6__edit_.pdf * * * gdi
+FRC_4.5.5_Pattern_shading.pdf * * * gdi
+FRC_4.5.5_Pattern_tiling.pdf * * * gdi
+FRC_4_8.2.4_Schema_8.6__remove_all.pdf * * * gdi
+FRC_5_8.2.4_Schema_8.6__remove_value.pdf * * * gdi
+FRC_6_8.2.4_Schema_8.6__remove_obj.pdf * * * gdi
+FRC_7_8.2.4_View_H.pdf * * * gdi
+FRC_8_8.2.4_View_D.pdf * * * gdi
+FRC_9_8.2.4_View_T.pdf * * * gdi
+annotation_circle_fill_opacity.pdf * * * gdi
+annotation_highlight_opacity.pdf * * * gdi
+annotation_square_fill_opacity.pdf * * * gdi
+annotation_square_fill_opacity_dash.pdf * * * gdi
+bug_0_length_line.pdf * * * gdi
+bug_668762.pdf * * * gdi
+bug_883026.pdf * * * gdi
+clipping_text.pdf * * * gdi
+en_fqa.pdf * * * gdi
+en_introduce.pdf * * * gdi
+en_system.pdf * * * gdi
+example_012.pdf * * * gdi
+gradient_many_stops.pdf * * * gdi
+group_xobject.pdf * * * gdi
+image_gif.pdf * * * gdi
+image_ico.pdf * * * gdi
+new_pdfsign1.pdf * * * gdi
+new_pdfsign2.pdf * * * gdi
+new_pdfsign3.pdf * * * gdi
+new_pdfsign4.pdf * * * gdi
+new_stamp3.pdf * * * gdi
+path_10_jd.pdf * * * gdi
+path_5_pattern.pdf * * * gdi
+path_7.pdf * * * gdi
+path_9.pdf * * * gdi
+transformation.pdf * * * gdi
+transparent.pdf * * * gdi
+transparent1.pdf * * * gdi
+xfermodes.pdf * * * gdi
+xfermodes2.pdf * * * gdi
+xfermodes3.pdf * * * gdi
+
 #
 # JavaScript tests
 #
@@ -333,6 +627,12 @@
 # Pixel tests
 #
 
+# TODO(pdfium:1098): Remove after associated bug is fixed
+bug_1098.in * * * *
+
+# TODO(chromium:1131694): Remove after associated bug is fixed
+bug_1131694.in * * * *
+
 # TODO(pdfium:1747): Remove after associated bug is fixed
 bug_1258634.in * * * *
 
@@ -342,6 +642,11 @@
 # TODO(chromium:1356149): Remove after associated bug is fixed
 bug_1356149.in mac * * agg
 
+# 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 * * *
+
 # TODO(pdfium:1457): Remove after associated bug is fixed
 bug_1457.in * * * *
 
@@ -398,3 +703,87 @@
 xfa_example.in win * * *
 # TODO(pdfium:1095): Remove after associated bug is fixed
 xfa_textfield.in win * * *
+
+# GDI
+
+# TODO(pdfium:2054): Remove after associated bug is fixed
+bug_1012369.in * * * gdi
+bug_1258968.in * * * gdi
+bug_1383708.in * * * gdi
+bug_1469.in * * * gdi
+bug_1693.in * * * gdi
+bug_1733.in * * * gdi
+bug_1750.in * * * gdi
+bug_512557.pdf * * * gdi
+bug_71.in * * * gdi
+bug_718762.in * * * gdi
+bug_867501.pdf * * * gdi
+bug_966263.in * * * gdi
+bug_986108.in * * * gdi
+image_transformer_other.in * * * gdi
+jpxdecode_without_smaskindata.in * * * gdi
+
+# TODO(pdfium:2055): Remove after associated bug is fixed
+barcode_test.pdf * * * gdi
+bug_1072440.in * * * gdi
+bug_113910.in * * * gdi
+bug_1282.in * * * gdi
+bug_1304714.in * * * gdi
+bug_1337.in * * * gdi
+bug_1372651.in * * * gdi
+bug_725389.in * * * gdi
+bug_733528.in * * * gdi
+bug_736695_1.in * * * gdi
+bug_736695_2.in * * * gdi
+bug_736695_3.in * * * gdi
+bug_736695_4.in * * * gdi
+bug_983137.in * * * gdi
+checkbox_radiobutton.in * * * gdi
+checkbox_radiobutton_hide.in * * * gdi
+checkbox_radiobutton_reset.in * * * gdi
+combobox_form.in * * * gdi
+dynamic_list_box_allow_multiple_selection.pdf * * * gdi
+dynamic_password_field_background_fill.pdf * * * gdi
+dynamic_table_color_and_width.pdf * * * gdi
+password.in * * * gdi
+resolve_nodes_0.pdf * * * gdi
+scrollable_widgets1.in * * * gdi
+scrollable_widgets2.in * * * gdi
+static_list_box_caption.pdf * * * gdi
+static_password_field_rotate.pdf * * * gdi
+text_form_custom_font.in * * * gdi
+xfa_bmp_image.in * * * gdi
+xfa_gif_image.in * * * gdi
+xfa_jpg_image.in * * * gdi
+xfa_node_caption.pdf * * * gdi
+xfa_png_image.in * * * gdi
+xfa_rectangle_node.in * * * gdi
+xfa_tiff_image.in * * * gdi
+xfa_tiff_lzw_image.in * * * gdi
+xfa_tiff_packbits_image.in * * * gdi
+
+# TODO(pdfium:2056): Remove after associated bug is fixed
+bug_1015233.in * * * gdi
+bug_1087.pdf * * * gdi
+bug_1099446.in * * * gdi
+bug_1161.in * * * gdi
+bug_1236.in * * * gdi
+bug_1288_1.in * * * gdi
+bug_1330.in * * * gdi
+bug_1395648.in * * * gdi
+bug_1396266.in * * * gdi
+bug_1430333.in * * * gdi
+bug_1822.in * * * gdi
+bug_1845.in * * * gdi
+bug_1847.in * * * gdi
+bug_1949.in * * * gdi
+bug_1966.in * * * gdi
+bug_1972_1.in * * * gdi
+bug_1972_3.in * * * gdi
+bug_1976.in * * * gdi
+bug_1995.in * * * gdi
+bug_451366.in * * * gdi
+bug_632.in * * * gdi
+bug_660850.in * * * gdi
+long_dashed_line.in * * * gdi
+matte.in * * * gdi
diff --git a/testing/SUPPRESSIONS_EXACT_MATCHING b/testing/SUPPRESSIONS_EXACT_MATCHING
index 7c260d1..b9734d9 100644
--- a/testing/SUPPRESSIONS_EXACT_MATCHING
+++ b/testing/SUPPRESSIONS_EXACT_MATCHING
@@ -10,7 +10,7 @@
 # Column 1: platform: *, win, mac, linux
 # Column 2: v8 support: *, nov8, v8
 # Column 3: xfa support: *, noxfa, xfa
-# Column 4: rendering support: *, agg, skia
+# Column 4: rendering support: *, agg, gdi, skia
 #
 # All columns on a line on a line must match, but filenames may be repeated
 # on subsequent lines to suppress more cases.  Within each column, any one of
diff --git a/testing/SUPPRESSIONS_IMAGE_DIFF b/testing/SUPPRESSIONS_IMAGE_DIFF
index e501756..e7ecd21 100644
--- a/testing/SUPPRESSIONS_IMAGE_DIFF
+++ b/testing/SUPPRESSIONS_IMAGE_DIFF
@@ -10,7 +10,7 @@
 # Column 1: platform: *, win, mac, linux
 # Column 2: v8 support: *, nov8, v8
 # Column 3: xfa support: *, noxfa, xfa
-# Column 4: rendering support: *, agg, skia
+# Column 4: rendering support: *, agg, gdi, skia
 #
 # All columns on a line on a line must match, but filenames may be repeated
 # on subsequent lines to suppress more cases.  Within each column, any one of
diff --git a/testing/allocator_shim_config.cpp b/testing/allocator_shim_config.cpp
new file mode 100644
index 0000000..8f0380d
--- /dev/null
+++ b/testing/allocator_shim_config.cpp
@@ -0,0 +1,25 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/allocator_shim_config.h"
+
+#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
+#include "base/allocator/partition_allocator/shim/allocator_shim.h"
+
+namespace pdfium {
+
+void ConfigurePartitionAllocShimPartitionForTest() {
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+  allocator_shim::ConfigurePartitions(
+      allocator_shim::EnableBrp(true),
+      allocator_shim::EnableMemoryTagging(false),
+      allocator_shim::SplitMainPartition(true),
+      allocator_shim::UseDedicatedAlignedPartition(true), 0,
+      allocator_shim::BucketDistribution::kNeutral);
+#endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+}
+
+}  // namespace pdfium
diff --git a/testing/allocator_shim_config.h b/testing/allocator_shim_config.h
new file mode 100644
index 0000000..cd14484
--- /dev/null
+++ b/testing/allocator_shim_config.h
@@ -0,0 +1,18 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_ALLOCATOR_SHIM_CONFIG_H_
+#define TESTING_ALLOCATOR_SHIM_CONFIG_H_
+
+#if !defined(PDF_USE_PARTITION_ALLOC)
+#error "Included under the wrong build options"
+#endif
+
+namespace pdfium {
+
+void ConfigurePartitionAllocShimPartitionForTest();
+
+}  // namespace pdfium
+
+#endif  // TESTING_ALLOCATOR_SHIM_CONFIG_H_
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index 366088d..4d31c91 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -24,8 +24,10 @@
 #include "testing/utils/hash.h"
 #include "testing/utils/path_service.h"
 #include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/containers/contains.h"
 #include "third_party/base/notreached.h"
+#include "third_party/base/numerics/checked_math.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
@@ -540,10 +542,8 @@
 void EmbedderTest::UnloadPageCommon(FPDF_PAGE page, bool do_events) {
   DCHECK(form_handle());
   int page_number = GetPageNumberForLoadedPage(page);
-  if (page_number < 0) {
-    NOTREACHED();
-    return;
-  }
+  CHECK_GE(page_number, 0);
+
   if (do_events) {
     FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_CLOSE);
     FORM_OnBeforeClosePage(page, form_handle());
@@ -563,10 +563,8 @@
 
 ScopedFPDFBitmap EmbedderTest::RenderLoadedPageWithFlags(FPDF_PAGE page,
                                                          int flags) {
-  if (GetPageNumberForLoadedPage(page) < 0) {
-    NOTREACHED();
-    return nullptr;
-  }
+  int page_number = GetPageNumberForLoadedPage(page);
+  CHECK_GE(page_number, 0);
   return RenderPageWithFlags(page, form_handle(), flags);
 }
 
@@ -576,10 +574,8 @@
 
 ScopedFPDFBitmap EmbedderTest::RenderSavedPageWithFlags(FPDF_PAGE page,
                                                         int flags) {
-  if (GetPageNumberForSavedPage(page) < 0) {
-    NOTREACHED();
-    return nullptr;
-  }
+  int page_number = GetPageNumberForSavedPage(page);
+  CHECK_GE(page_number, 0);
   return RenderPageWithFlags(page, saved_form_handle(), flags);
 }
 
@@ -681,8 +677,7 @@
     case FPDFBitmap_BGRA:
       return 4;
     default:
-      NOTREACHED();
-      return 0;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -733,10 +728,7 @@
   DCHECK(saved_form_handle());
 
   int page_number = GetPageNumberForSavedPage(page);
-  if (page_number < 0) {
-    NOTREACHED();
-    return;
-  }
+  CHECK_GE(page_number, 0);
 
   FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_CLOSE);
   FORM_OnBeforeClosePage(page, saved_form_handle());
@@ -853,15 +845,11 @@
                                      unsigned char* buf,
                                      unsigned long size) {
   std::string* new_file = static_cast<std::string*>(param);
-  if (!new_file || pos + size < pos) {
-    NOTREACHED();
-    return 0;
-  }
+  CHECK(new_file);
 
-  if (pos + size > new_file->size()) {
-    NOTREACHED();
-    return 0;
-  }
+  pdfium::base::CheckedNumeric<size_t> end = pos;
+  end += size;
+  CHECK_LE(end.ValueOrDie(), new_file->size());
 
   memcpy(buf, new_file->data() + pos, size);
   return 1;
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index c73dd61..829b1aa 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -21,7 +21,7 @@
 #include "testing/fake_file_access.h"
 #include "testing/free_deleter.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class TestLoader;
 
diff --git a/testing/embedder_test_constants.cpp b/testing/embedder_test_constants.cpp
index 9e8caee..1364de1 100644
--- a/testing/embedder_test_constants.cpp
+++ b/testing/embedder_test_constants.cpp
@@ -11,7 +11,7 @@
 
 const char* AnnotationStampWithApChecksum() {
   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "a31381406d0b95049e418720750b78dd";
+    return "c7ff65a3ad1b01c3a0e94d635f10670e";
 #if BUILDFLAG(IS_APPLE)
   return "0521eaa52fe2aa43aafd3e4495f63f0b";
 #else
@@ -28,19 +28,25 @@
 }
 
 const char* HelloWorldChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "d1decde2de1c07b5274cc8cb44f92427";
+  }
 #if BUILDFLAG(IS_APPLE)
-  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "6eef7237f7591f07616e238422086737";
-#endif
+  return "6eef7237f7591f07616e238422086737";
+#else
   return "c1c548442e0e0f949c5550d89bf8ae3b";
+#endif
 }
 
 const char* HelloWorldRemovedChecksum() {
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    return "6e0307348e7c1b92f2f061f92f62fd45";
+  }
 #if BUILDFLAG(IS_APPLE)
-  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "6e1cae48a2e35c521dee4ca502f48af6";
-#endif
+  return "6e1cae48a2e35c521dee4ca502f48af6";
+#else
   return "4a9b80f675f7f3bf2da1b02f12449e4b";
+#endif
 }
 
 const char* ManyRectanglesChecksum() {
@@ -57,7 +63,7 @@
 
 const char* TextFormChecksum() {
   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-    return "e6d2eb75f18d773f0dad938b1bb22e23";
+    return "b259776fd156003e2a594d1c7ce2d8d7";
 #if BUILDFLAG(IS_APPLE)
   return "fa2bf756942a950101fc147fc4ef3f82";
 #else
diff --git a/testing/embedder_test_main.cpp b/testing/embedder_test_main.cpp
index 53049b6..e089b1a 100644
--- a/testing/embedder_test_main.cpp
+++ b/testing/embedder_test_main.cpp
@@ -12,10 +12,18 @@
 #include "testing/v8_test_environment.h"
 #endif  // PDF_ENABLE_V8
 
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "testing/allocator_shim_config.h"
+#endif
+
 // Can't use gtest-provided main since we need to create our own
 // testing environment which needs the executable path in order to
 // find the external V8 binary data files.
 int main(int argc, char** argv) {
+#if defined(PDF_USE_PARTITION_ALLOC)
+  pdfium::ConfigurePartitionAllocShimPartitionForTest();
+#endif
+
   FX_InitializeMemoryAllocators();
 
 #ifdef PDF_ENABLE_V8
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index 35624cb..698987f 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build_overrides/build.gni")
 import("../../pdfium.gni")
 
 config("fuzzer_config") {
@@ -247,9 +246,6 @@
     if (is_public && pdf_enable_xfa) {
       deps += [ ":fuzzer_xfa_process_state" ]
     }
-    if (build_with_chromium) {
-      defines += [ "BUILD_WITH_CHROMIUM" ]
-    }
   }
 }
 
@@ -620,11 +616,5 @@
 pdfium_fuzzer("pdfium_fuzzer") {
   sources = [ "pdfium_fuzzer.cc" ]
   deps = [ ":fuzzer_helper" ]
-  if (build_with_chromium) {
-    deps += [
-      "//base",
-      "//base/test:test_support",
-    ]
-  }
   public_fuzzer = true
 }
diff --git a/testing/fuzzers/DEPS b/testing/fuzzers/DEPS
index d577044..fe9eaf6 100644
--- a/testing/fuzzers/DEPS
+++ b/testing/fuzzers/DEPS
@@ -1,7 +1,4 @@
 include_rules = [
   '+fxbarcode',
   '+xfa',
-
-  # Only used when the fuzzer is embedded in Chromium.
-  '+base',
 ]
diff --git a/testing/fuzzers/pdf_cmap_fuzzer.cc b/testing/fuzzers/pdf_cmap_fuzzer.cc
index d4f9c70..58dfbe6 100644
--- a/testing/fuzzers/pdf_cmap_fuzzer.cc
+++ b/testing/fuzzers/pdf_cmap_fuzzer.cc
@@ -6,7 +6,7 @@
 
 #include "core/fpdfapi/font/cpdf_cmap.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   if (size > 256 * 1024)
diff --git a/testing/fuzzers/pdf_codec_icc_fuzzer.cc b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
index 4db79d2..ada4bc7 100644
--- a/testing/fuzzers/pdf_codec_icc_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
@@ -5,7 +5,7 @@
 #include <cstdint>
 
 #include "core/fxcodec/icc/icc_transform.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   std::unique_ptr<fxcodec::IccTransform> transform =
diff --git a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
index 59eda76..4c48b6f 100644
--- a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
@@ -38,7 +38,7 @@
   Jbig2Context jbig2_context;
   FXCODEC_STATUS status = Jbig2Decoder::StartDecode(
       &jbig2_context, &document_context, width, height, {data, size}, 1, {}, 0,
-      bitmap->GetBuffer(), bitmap->GetPitch(), nullptr);
+      bitmap->GetWritableBuffer(), bitmap->GetPitch(), nullptr);
 
   while (status == FXCODEC_STATUS::kDecodeToBeContinued)
     status = Jbig2Decoder::ContinueDecode(&jbig2_context, nullptr);
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc
index 3227d47..993c8a7 100644
--- a/testing/fuzzers/pdf_fuzzer_init_public.cc
+++ b/testing/fuzzers/pdf_fuzzer_init_public.cc
@@ -36,8 +36,9 @@
 namespace {
 
 // pdf_fuzzer_init.cc and pdf_fuzzer_init_public.cc are mutually exclusive
-// and should not be built together.
-PDFFuzzerInitPublic* g_instance = new PDFFuzzerInitPublic();
+// and should not be built together. Static initializers and destructors
+// avoid problems with fuzzer initialization and termination.
+PDFFuzzerInitPublic g_instance;
 
 #ifdef PDF_ENABLE_V8
 std::string ProgramPath() {
diff --git a/testing/fuzzers/pdf_hint_table_fuzzer.cc b/testing/fuzzers/pdf_hint_table_fuzzer.cc
index 3743b1a..1351a04 100644
--- a/testing/fuzzers/pdf_hint_table_fuzzer.cc
+++ b/testing/fuzzers/pdf_hint_table_fuzzer.cc
@@ -11,7 +11,7 @@
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fxcrt/cfx_bitstream.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 int32_t GetData(const int32_t** data32, const uint8_t** data, size_t* size) {
   const int32_t* ret = *data32;
diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc
index 3021d76..c7e7e2f 100644
--- a/testing/fuzzers/pdf_jpx_fuzzer.cc
+++ b/testing/fuzzers/pdf_jpx_fuzzer.cc
@@ -69,8 +69,8 @@
           static_cast<uint32_t>(bitmap->GetHeight()))
     return 0;
 
-  decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(), /*swap_rgb=*/false,
-                  GetCompsFromFormat(format));
+  decoder->Decode(bitmap->GetWritableBuffer(), bitmap->GetPitch(),
+                  /*swap_rgb=*/false, GetCompsFromFormat(format));
 
   return 0;
 }
diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc
index bc141ed..43c10c7 100644
--- a/testing/fuzzers/pdf_nametree_fuzzer.cc
+++ b/testing/fuzzers/pdf_nametree_fuzzer.cc
@@ -11,7 +11,7 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfdoc/cpdf_nametree.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 struct Params {
   bool delete_backwards;
diff --git a/testing/fuzzers/pdf_psengine_fuzzer.cc b/testing/fuzzers/pdf_psengine_fuzzer.cc
index 5cc2a76..f24bdce 100644
--- a/testing/fuzzers/pdf_psengine_fuzzer.cc
+++ b/testing/fuzzers/pdf_psengine_fuzzer.cc
@@ -5,7 +5,7 @@
 #include <stdint.h>
 
 #include "core/fpdfapi/page/cpdf_psengine.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   CPDF_PSEngine engine;
diff --git a/testing/fuzzers/pdf_streamparser_fuzzer.cc b/testing/fuzzers/pdf_streamparser_fuzzer.cc
index e3bd787..6921cc9 100644
--- a/testing/fuzzers/pdf_streamparser_fuzzer.cc
+++ b/testing/fuzzers/pdf_streamparser_fuzzer.cc
@@ -6,7 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_streamparser.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   CPDF_StreamParser parser(pdfium::make_span(data, size));
diff --git a/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
index c438c89..cefd09d 100644
--- a/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
+++ b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
@@ -159,9 +159,6 @@
           FORM_GetFocusedText(form, page, local_buf, sizeof(local_buf));
           break;
         }
-        default: {
-          break;
-        }
       }
     }
   }
diff --git a/testing/fuzzers/pdf_xml_fuzzer.cc b/testing/fuzzers/pdf_xml_fuzzer.cc
index 31ab6d7..e908a7e 100644
--- a/testing/fuzzers/pdf_xml_fuzzer.cc
+++ b/testing/fuzzers/pdf_xml_fuzzer.cc
@@ -12,7 +12,7 @@
 #include "core/fxcrt/xml/cfx_xmldocument.h"
 #include "core/fxcrt/xml/cfx_xmlelement.h"
 #include "core/fxcrt/xml/cfx_xmlparser.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   FX_SAFE_SIZE_T safe_size = size;
diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc
index 7e25fed..e70702a 100644
--- a/testing/fuzzers/pdfium_fuzzer.cc
+++ b/testing/fuzzers/pdfium_fuzzer.cc
@@ -6,12 +6,6 @@
 
 #include "testing/fuzzers/pdfium_fuzzer_helper.h"
 
-#if defined(BUILD_WITH_CHROMIUM)
-#include "base/memory/discardable_memory_allocator.h"
-#include "base/no_destructor.h"
-#include "base/test/test_discardable_memory_allocator.h"
-#endif
-
 class PDFiumFuzzer : public PDFiumFuzzerHelper {
  public:
   PDFiumFuzzer() = default;
@@ -21,12 +15,6 @@
 };
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-#if defined(BUILD_WITH_CHROMIUM)
-  static base::NoDestructor<base::TestDiscardableMemoryAllocator>
-      test_memory_allocator;
-  base::DiscardableMemoryAllocator::SetInstance(test_memory_allocator.get());
-#endif
-
   PDFiumFuzzer fuzzer;
   fuzzer.RenderPdf(reinterpret_cast<const char*>(data), size);
   return 0;
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc
index f011b18..1d26787 100644
--- a/testing/fuzzers/pdfium_fuzzer_helper.cc
+++ b/testing/fuzzers/pdfium_fuzzer_helper.cc
@@ -20,8 +20,9 @@
 #include "public/fpdf_dataavail.h"
 #include "public/fpdf_ext.h"
 #include "public/fpdf_text.h"
-#include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/span.h"
+#include "third_party/base/numerics/checked_math.h"
 
 namespace {
 
@@ -34,10 +35,9 @@
                       unsigned char* pBuf,
                       unsigned long size) {
     FuzzerTestLoader* pLoader = static_cast<FuzzerTestLoader*>(param);
-    if (pos + size < pos || pos + size > pLoader->m_Span.size()) {
-      NOTREACHED();
-      return 0;
-    }
+    pdfium::base::CheckedNumeric<size_t> end = pos;
+    end += size;
+    CHECK_LE(end.ValueOrDie(), pLoader->m_Span.size());
 
     memcpy(pBuf, &pLoader->m_Span[pos], size);
     return 1;
diff --git a/testing/fuzzers/xfa_codec_fuzzer.h b/testing/fuzzers/xfa_codec_fuzzer.h
index b14b7d7..aaa884b 100644
--- a/testing/fuzzers/xfa_codec_fuzzer.h
+++ b/testing/fuzzers/xfa_codec_fuzzer.h
@@ -14,7 +14,7 @@
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 // Support up to 64 MB. This prevents trivial OOM when MSAN is on and
 // time outs.
diff --git a/testing/fx_string_testhelpers.cpp b/testing/fx_string_testhelpers.cpp
index 8e76a76..09f7653 100644
--- a/testing/fx_string_testhelpers.cpp
+++ b/testing/fx_string_testhelpers.cpp
@@ -11,7 +11,7 @@
 #include "core/fxcrt/cfx_datetime.h"
 #include "core/fxcrt/fx_string.h"
 #include "third_party/base/check_op.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt) {
   os << dt.GetYear() << "-" << std::to_string(dt.GetMonth()) << "-"
@@ -76,6 +76,6 @@
 }
 
 std::vector<FPDF_WCHAR> GetFPDFWideStringBuffer(size_t length_bytes) {
-  DCHECK_EQ(length_bytes % sizeof(FPDF_WCHAR), 0);
+  DCHECK_EQ(length_bytes % sizeof(FPDF_WCHAR), 0u);
   return std::vector<FPDF_WCHAR>(length_bytes / sizeof(FPDF_WCHAR));
 }
diff --git a/testing/image_diff/image_diff.cpp b/testing/image_diff/image_diff.cpp
index a701921..a203bf5 100644
--- a/testing/image_diff/image_diff.cpp
+++ b/testing/image_diff/image_diff.cpp
@@ -21,7 +21,6 @@
 #include "core/fxcrt/fx_memory.h"
 #include "testing/image_diff/image_diff_png.h"
 #include "testing/utils/path_service.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -351,9 +350,9 @@
       int32_t delta_b = b1 - b2;
       same &= (delta_r == 0 && delta_g == 0 && delta_b == 0);
 
-      delta_r = pdfium::clamp(128 + delta_r * 8, 0, 255);
-      delta_g = pdfium::clamp(128 + delta_g * 8, 0, 255);
-      delta_b = pdfium::clamp(128 + delta_b * 8, 0, 255);
+      delta_r = std::clamp(128 + delta_r * 8, 0, 255);
+      delta_g = std::clamp(128 + delta_g * 8, 0, 255);
+      delta_b = std::clamp(128 + delta_b * 8, 0, 255);
 
       uint32_t new_pixel = RGBA_ALPHA;
       new_pixel |= delta_r;
@@ -369,11 +368,17 @@
                const std::string& file1,
                const std::string& file2,
                const std::string& out_file,
-               bool do_subtraction) {
+               bool do_subtraction,
+               bool reverse_byte_order) {
   Image actual_image;
   Image baseline_image;
 
-  if (!actual_image.CreateFromFilename(file1)) {
+  bool actual_load_result =
+      reverse_byte_order
+          ? actual_image.CreateFromFilenameWithReverseByteOrder(file1)
+          : actual_image.CreateFromFilename(file1);
+
+  if (!actual_load_result) {
     fprintf(stderr, "%s: Unable to open file \"%s\"\n", binary_name.c_str(),
             file1.c_str());
     return kStatusError;
@@ -451,7 +456,7 @@
   if (produce_diff_image || produce_image_subtraction) {
     if (!diff_filename.empty()) {
       return DiffImages(binary_name, filename1, filename2, diff_filename,
-                        produce_image_subtraction);
+                        produce_image_subtraction, reverse_byte_order);
     }
   } else if (!filename2.empty()) {
     return CompareImages(binary_name, filename1, filename2, histograms,
diff --git a/testing/image_diff/image_diff_png.cpp b/testing/image_diff/image_diff_png.cpp
index c2e8b03..5a89e35 100644
--- a/testing/image_diff/image_diff_png.cpp
+++ b/testing/image_diff/image_diff_png.cpp
@@ -16,6 +16,7 @@
 
 #include <string>
 
+#include "third_party/base/check_op.h"
 #include "third_party/base/notreached.h"
 
 #ifdef USE_SYSTEM_ZLIB
@@ -253,8 +254,7 @@
         state->output_channels = 1;
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   } else if (channels == 4) {
     switch (state->output_format) {
@@ -271,12 +271,10 @@
         state->output_channels = 4;
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   } else {
-    NOTREACHED();
-    longjmp(png_jmpbuf(png_ptr), 1);
+    NOTREACHED_NORETURN();
   }
 
   state->output->resize(state->width * state->output_channels * state->height);
@@ -288,11 +286,7 @@
                        int pass) {
   PngDecoderState* state =
       static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
-
-  if (static_cast<int>(row_num) > state->height) {
-    NOTREACHED();
-    return;
-  }
+  CHECK_LE(static_cast<int>(row_num), state->height);
 
   uint8_t* base = nullptr;
   base = &state->output->front();
@@ -609,10 +603,6 @@
       output_color_components = 1;
       png_output_color_type = PNG_COLOR_TYPE_GRAY;
       break;
-
-    default:
-      NOTREACHED();
-      return output;
   }
 
   // Row stride should be at least as long as the length of the data.
diff --git a/testing/image_diff/image_diff_png.h b/testing/image_diff/image_diff_png.h
index 370ce32..5d54c95 100644
--- a/testing/image_diff/image_diff_png.h
+++ b/testing/image_diff/image_diff_png.h
@@ -9,7 +9,7 @@
 
 #include <vector>
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 namespace image_diff_png {
 
diff --git a/testing/resources/bug_1658.in b/testing/resources/bug_1658.in
new file mode 100644
index 0000000..0301c9d
--- /dev/null
+++ b/testing/resources/bug_1658.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /MediaBox [0 0 400 400]
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 5 0 R
+  >>
+  /F 8
+  /P 3 0 R
+  /Rect 6 0 R
+>>
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox 6 0 R
+  /ProcSet [/PDF]
+  /Resources <<
+    /ExtGState <<
+      /R0 <<
+        /Type /ExtGState
+        /AIS false
+        /BM /Multiply
+      >>
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+/R0 gs
+1 1 0 rg
+1 w
+72 305 m
+68 309 68 317 72 321 c
+132 321 l
+136 317 136 309 132 305 c
+f
+endstream
+endobj
+{{object 6 0}}
+[67 304 137 322]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1658.pdf b/testing/resources/bug_1658.pdf
new file mode 100644
index 0000000..8c47b65
--- /dev/null
+++ b/testing/resources/bug_1658.pdf
@@ -0,0 +1,78 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /MediaBox [0 0 400 400]
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Highlight
+  /AP <<
+    /N 5 0 R
+  >>
+  /F 8
+  /P 3 0 R
+  /Rect 6 0 R
+>>
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /FormType 1
+  /BBox 6 0 R
+  /ProcSet [/PDF]
+  /Resources <<
+    /ExtGState <<
+      /R0 <<
+        /Type /ExtGState
+        /AIS false
+        /BM /Multiply
+      >>
+    >>
+  >>
+  /Length 90
+>>
+stream
+/R0 gs
+1 1 0 rg
+1 w
+72 305 m
+68 309 68 317 72 321 c
+132 321 l
+136 317 136 309 132 305 c
+f
+endstream
+endobj
+6 0 obj
+[67 304 137 322]
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000226 00000 n 
+0000000343 00000 n 
+0000000698 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+730
+%%EOF
diff --git a/testing/resources/bug_2034.idat b/testing/resources/bug_2034.idat
new file mode 100644
index 0000000..873c408
--- /dev/null
+++ b/testing/resources/bug_2034.idat
Binary files differ
diff --git a/testing/resources/bug_2034.in b/testing/resources/bug_2034.in
new file mode 100644
index 0000000..f762659
--- /dev/null
+++ b/testing/resources/bug_2034.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Resources <<
+    /XObject <<
+      /HighResolutionImage 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+  612 0 0 792 0 0 cm
+  /HighResolutionImage Do
+Q
+endstream
+endobj
+
+% Image meant to simulate scanner output: full page, 600 DPI, in RGB color.
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 4
+  /ColorSpace [
+    /Indexed
+    /DeviceRGB
+    4
+    <000000 FF0000 00FF00 0000FF FFFFFF>
+  ]
+  /DecodeParms <<
+    /BitsPerComponent 4
+    /Columns 5100
+    /Predictor 10
+  >>
+  /Filter /FlateDecode
+  /Height 6600
+  /Width 5100
+  {{streamlen}}
+>>
+stream
+{{include bug_2034.idat}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_2034.pdf b/testing/resources/bug_2034.pdf
new file mode 100644
index 0000000..78ddedf
--- /dev/null
+++ b/testing/resources/bug_2034.pdf
Binary files differ
diff --git a/testing/resources/javascript/bug_1447268.evt b/testing/resources/javascript/bug_1447268.evt
new file mode 100644
index 0000000..be4c96c
--- /dev/null
+++ b/testing/resources/javascript/bug_1447268.evt
@@ -0,0 +1,2 @@
+mouseup,left,107,521
+mousedown,left,108,521
diff --git a/testing/resources/javascript/bug_1447268.in b/testing/resources/javascript/bug_1447268.in
new file mode 100644
index 0000000..b12a389
--- /dev/null
+++ b/testing/resources/javascript/bug_1447268.in
@@ -0,0 +1,209 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm 4 0 R
+  /OpenAction 40 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 5
+  /Kids [
+    30 0 R
+    31 0 R
+    32 0 R
+    33 0 R
+    34 0 R
+  ]
+>>
+endobj
+% Forms
+{{object 4 0}} <<
+  /Fields [
+    5 0 R
+    8 0 R
+    9 0 R
+    10 0 R
+    11 0 R
+  ]
+>>
+endobj
+% Fields
+{{object 5 0}} <<
+  /T (Field)
+  /Kids [6 0 R]
+  /V (my_field)
+>>
+endobj
+{{object 6 0}} <<
+  /FT /Tx
+  /Parent 5 0 R
+  /Kids [7 0 R]
+  /Rect [200 200 220 220]
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /Parent 6 0 R
+  /Rect [0 500 600 600]
+
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Field2)
+  /V (Field_2)
+  /Rect [0 500 600 600]
+>>
+endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Field4)
+  /V (Field_4)
+  /Rect [0 500 600 600]
+  /AA << /F 24 0 R >>
+>>
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /T (Field5)
+  /V (Field_5)
+  /Rect [0 500 600 600]
+>>
+endobj
+{{object 11 0}} <<
+  /T (Field3)
+  /Parent 4 0 R
+  /Kids [12 0 R]
+  /Opt [(aa) (bb) (cc) (dd) (ee)]
+  /V [(aa) (bb) (cc) (dd) (ee)]
+>>
+endobj
+{{object 12 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Ch
+  /Ff 131072
+  /Parent 11 0 R
+  /Kids [13 0 R]
+>>
+endobj
+{{object 13 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /Parent 12 0 R
+  /Rect [0 400 600 600]
+>>
+endobj
+{{object 14 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /Parent 12 0 R
+  /Rect [100 400 500 500]
+>>
+endobj
+% OpenAction action
+{{object 22 0}} <<
+  /S /JavaScript
+  /JS 23 0 R
+>>
+endobj
+{{object 23 0}} <<
+  {{streamlen}}
+>>
+stream
+doc = this;
+function cb_func() {
+    doc.pageNum = 1;
+    doc.getField("Field" ).setFocus();
+    doc.getField("Field1").setFocus();
+    doc.getField("Field2").setFocus();
+    doc.getField("Field3").setFocus();
+    doc.getField("Field4").setFocus();
+    doc.getField("Field5").setFocus();
+    return 0;
+}
+doc.getField("Field").checkThisBox ({valueOf: cb_func});
+doc.getField("Field1").checkThisBox({valueOf: cb_func});
+doc.getField("Field2").checkThisBox({valueOf: cb_func});
+doc.getField("Field3").checkThisBox({valueOf: cb_func});
+doc.getField("Field4").checkThisBox({valueOf: cb_func});
+doc.getField("Field5").checkThisBox({valueOf: cb_func});
+endstream
+endobj
+% OpenAction action
+{{object 24 0}} <<
+  /S /JavaScript
+  /JS 25 0 R
+>>
+endobj
+{{object 25 0}} <<
+  {{streamlen}}
+>>
+stream
+this.pageNum = 2;
+this.pageNum = 3;
+endstream
+endobj
+% Pages
+{{object 30 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 31 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [9 0 R]
+>>
+endobj
+{{object 32 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [13 0 R]
+>>
+endobj
+{{object 33 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <</C 22 0 R>>
+>>
+endobj
+{{object 34 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [10 0 R]
+>>
+endobj
+% Document JS Action
+{{object 40 0}} <<
+  /Type /Action
+  /S /JavaScript
+  /JS 41 0 R
+>>
+endobj
+{{object 41 0}} <<
+  {{streamlen}}
+>>
+stream
+var f = this.getField("Field4");
+f.setFocus();
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1447268_expected.txt b/testing/resources/javascript/bug_1447268_expected.txt
new file mode 100644
index 0000000..5d198fe
--- /dev/null
+++ b/testing/resources/javascript/bug_1447268_expected.txt
@@ -0,0 +1,3 @@
+Goto Page: 2
+Goto Page: 3
+Goto Page: 1
diff --git a/testing/resources/multiple_graphics_states.in b/testing/resources/multiple_graphics_states.in
new file mode 100644
index 0000000..882495f
--- /dev/null
+++ b/testing/resources/multiple_graphics_states.in
@@ -0,0 +1,58 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ExtGState <<
+      /GS1 5 0 R
+      /GS2 6 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+/GS1 gs /GS2 gs
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /ca 0.25
+>>
+endobj
+{{object 6 0}} <<
+  /LW 4
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/multiple_graphics_states.pdf b/testing/resources/multiple_graphics_states.pdf
new file mode 100644
index 0000000..d9a8d4a
--- /dev/null
+++ b/testing/resources/multiple_graphics_states.pdf
@@ -0,0 +1,71 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /ExtGState <<
+      /GS1 5 0 R
+      /GS2 6 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 204
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+/GS1 gs /GS2 gs
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+5 0 obj <<
+  /ca 0.25
+>>
+endobj
+6 0 obj <<
+  /LW 4
+>>
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000306 00000 n 
+0000000562 00000 n 
+0000000594 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+623
+%%EOF
diff --git a/testing/resources/pixel/bug_1021762_expected_skia_linux.pdf.0.png b/testing/resources/pixel/bug_1021762_expected_skia_linux.pdf.0.png
new file mode 100644
index 0000000..1419d2e
--- /dev/null
+++ b/testing/resources/pixel/bug_1021762_expected_skia_linux.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1021762_expected_skia_win.pdf.0.png b/testing/resources/pixel/bug_1021762_expected_skia_win.pdf.0.png
new file mode 100644
index 0000000..1419d2e
--- /dev/null
+++ b/testing/resources/pixel/bug_1021762_expected_skia_win.pdf.0.png
Binary files 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 6ce4fbe..b0d2d8d 100644
--- a/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1098.evt b/testing/resources/pixel/bug_1098.evt
new file mode 100644
index 0000000..0e0538b
--- /dev/null
+++ b/testing/resources/pixel/bug_1098.evt
@@ -0,0 +1,2 @@
+# Show text annotation pop-up
+mousemove,20,250
diff --git a/testing/resources/pixel/bug_1098.in b/testing/resources/pixel/bug_1098.in
new file mode 100644
index 0000000..a18aa19
--- /dev/null
+++ b/testing/resources/pixel/bug_1098.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /AcroForm 2 0 R
+  /Pages 3 0 R
+>>
+{{object 2 0}} <<
+  /Fields [6 0 R]
+  /NeedAppearances true
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 3 0 R
+  /Annots [5 0 R 6 0 R]
+  /MediaBox [0 0 220 270]
+>>
+endobj
+
+% Text annotation with a pop-up containing the test string.
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Text
+  /Contents 7 0 R
+
+  % GenerateTextAP() always uses a 20x20 rectangle for the note icon.
+  % CreatePopupAnnot() always uses a 200x200 rectangle for the pop-up.
+  /Rect [10 240 30 260]
+>>
+endobj
+
+% Text field widget annotation with a black border containing the test string.
+{{object 6 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Tx
+  /MK <<
+    /BC [0]
+  >>
+  /Rect [10 10 210 30]
+  /T (FieldName)
+  /V 7 0 R
+>>
+endobj
+
+% Test string "AaÄäКк€🎨!" in UTF-16BE.
+{{object 7 0}}
+<feff 0041 0061 00c4 00e4 041a 043a 20ac d83cdfa8 0021>
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1098_expected.pdf.0.png b/testing/resources/pixel/bug_1098_expected.pdf.0.png
new file mode 100644
index 0000000..0c518b2
--- /dev/null
+++ b/testing/resources/pixel/bug_1098_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1098_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1098_expected_mac.pdf.0.png
new file mode 100644
index 0000000..39532a1
--- /dev/null
+++ b/testing/resources/pixel/bug_1098_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1098_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1098_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8884a3f
--- /dev/null
+++ b/testing/resources/pixel/bug_1098_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1098_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_1098_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..e02cdf9
--- /dev/null
+++ b/testing/resources/pixel/bug_1098_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1131694.in b/testing/resources/pixel/bug_1131694.in
new file mode 100644
index 0000000..98ad7fc
--- /dev/null
+++ b/testing/resources/pixel/bug_1131694.in
@@ -0,0 +1,125 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /MediaBox [0 0 200 200]
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+    /Pattern <<
+      /P1 8 0 R
+    >>
+    /XObject <<
+      /Img 9 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+/Pattern CS
+/Pattern cs
+/P1 SCN
+/P1 scn
+BT
+/F1 2 Tf
+1 0 0 1 50 50 Tm
+(a)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type3
+  /CharProcs <<
+    /a0 7 0 R
+  >>
+  /Encoding 6 0 R
+  /FirstChar 97
+  /FontBBox [0 0 1000 1000]
+  /FontMatrix [1 0 0 1 0 0]
+  /LastChar 97
+  /Widths [60]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /Encoding
+  /BaseEncoding /WinAnsiEncoding
+  /Differences [97 /a0]
+>>
+endobj
+{{object 7 0}} <<
+  {{streamlen}}
+>>
+stream
+50 0 4 -1 48 45 d1
+q
+4 -1 m
+4 45 l
+48 45 l
+48 -1 l
+h
+W n
+q
+44 0 0 46 4.1 -1.1 cm
+/Img Do
+Q
+Q
+endstream
+endobj
+{{object 8 0}} <<
+  /Type /Pattern
+  /PatternType 2
+  /Matrix [0.75 0 0 -0.75 77 691]
+  /Shading <<
+    /ShadingType 2
+    /ColorSpace /DeviceRGB
+    /Coords [0 0 0 62]
+    /Extend [true true]
+    /Function <<
+      /FunctionType 2
+      /C0 [0.7 0.5 0.3]
+      /C1 [0.9 0.7 0.5]
+      /Domain [0 1]
+      /N 1
+    >>
+  >>
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /BitsPerComponent 1
+  /Decode [1 0]
+  /DecodeParms [null <</Columns 44 /K -1>>]
+  /Filter [/ASCIIHexDecode /CCITTFaxDecode]
+  /Height 46
+  /ImageMask true
+  /Width 44
+  {{streamlen}}
+>>
+stream
+26a08680de081e11e187840830f4137a4df0ef4dbedbffff6ffdaf0c2f1f
+fff21b34c82e33044e7c11a09c205e105e97a5e97a5ffa5fe0a3f5ffafff
+b5c9aaa30bed27adb4ad70da4da5b6128f0c426b216818500100100a
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1131694_expected.pdf.0.png b/testing/resources/pixel/bug_1131694_expected.pdf.0.png
new file mode 100644
index 0000000..37b4cdd
--- /dev/null
+++ b/testing/resources/pixel/bug_1131694_expected.pdf.0.png
Binary files 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 3c3cd81..4eb3fa0 100644
--- a/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1271578_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1271578_expected_skia.pdf.0.png
new file mode 100644
index 0000000..b37b9ec
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1286_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1286_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..c20b974
--- /dev/null
+++ b/testing/resources/pixel/bug_1286_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1287409_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1287409_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..72da0cd
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_2_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1288_2_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..0433e4d
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1296_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1296_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..d261930
--- /dev/null
+++ b/testing/resources/pixel/bug_1296_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1_expected_skia_linux.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected_skia_linux.pdf.0.png
new file mode 100644
index 0000000..784332c
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_1_expected_skia_linux.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1_expected_skia_win.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected_skia_win.pdf.0.png
new file mode 100644
index 0000000..784332c
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_1_expected_skia_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_skia_linux.pdf.0.png b/testing/resources/pixel/bug_1308_expected_skia_linux.pdf.0.png
new file mode 100644
index 0000000..252d0a8
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_expected_skia_linux.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_skia_win.pdf.0.png b/testing/resources/pixel/bug_1308_expected_skia_win.pdf.0.png
new file mode 100644
index 0000000..252d0a8
--- /dev/null
+++ b/testing/resources/pixel/bug_1308_expected_skia_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1338_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1338_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..5fc2b32
--- /dev/null
+++ b/testing/resources/pixel/bug_1338_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png
index dae811b..f097788 100644
--- a/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1355_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_1355_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..dae811b
--- /dev/null
+++ b/testing/resources/pixel/bug_1355_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1356149_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1356149_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8f67a34
--- /dev/null
+++ b/testing/resources/pixel/bug_1356149_expected_skia.pdf.0.png
Binary files 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 f1823f3..e7c07b7 100644
--- a/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..c5270f8
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected_skia.pdf.0.png
new file mode 100644
index 0000000..b97bdeb
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_3_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1388_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..7753a6f
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png
index 80a944b..e62f06b 100644
--- a/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png
index e872c2c..4d483cb 100644
--- a/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png
index f34b78e..9d2c92e 100644
--- a/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_1402_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..f34b78e
--- /dev/null
+++ b/testing/resources/pixel/bug_1402_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1442723.in b/testing/resources/pixel/bug_1442723.in
new file mode 100644
index 0000000..070c97c
--- /dev/null
+++ b/testing/resources/pixel/bug_1442723.in
@@ -0,0 +1,80 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 200 100]
+  /Resources <<
+    /Font <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+75 50 TD
+/F1 12 Tf
+10 Tc (=\331\367\370\366\347) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /TrueType
+  /BaseFont /Symbol
+  /FirstChar 31
+  /FontDescriptor 6 0 R
+  /LastChar 255
+  /Widths [600 250 333 713 500 549 833 778 439 333 333 500 549 250 549 250 278
+           500 500 500 500 500 500 500 500 500 500 278 278 549 549 549 444 549
+           722 667 722 612 611 763 603 722 333 631 722 686 889 722 722 768 741
+           556 592 611 690 439 768 645 795 611 333 863 333 658 500 500 631 549
+           549 494 439 521 411 603 329 603 549 549 576 521 549 549 521 549 603
+           439 576 713 686 493 686 494 480 200 480 549 600 600 600 600 600 600
+           600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600
+           600 600 600 600 600 600 600 600 600 600 600 620 247 549 167 713 500
+           753 753 753 753 1042 987 603 987 603 400 549 411 549 549 713 494 460
+           549 549 549 549 1000 603 1000 658 823 686 795 987 768 768 823 768 768
+           713 713 713 713 713 713 713 768 713 790 790 890 823 549 250 713 603
+           603 1042 987 603 987 603 494 329 790 790 786 713 384 384 384 384 384
+           384 494 494 494 494 600 329 274 686 686 686 384 384 384 384 384 384
+           494 494 494 600]
+>>
+endobj
+{{object 6 0}} <<
+  /Type /FontDescriptor
+  /Ascent 1000
+  /AvgWidth 602
+  /CapHeight 1000
+  /Descent -217
+  /Flags 6
+  /FontBBox [-250 -217 1258 1000]
+  /FontName /Symbol
+  /ItalicAngle 0
+  /Leading 217
+  /MaxWidth 1048
+  /MissingWidth 602
+  /StemH 110
+  /StemV 110
+  /XHeight 700
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1442723_expected.pdf.0.png b/testing/resources/pixel/bug_1442723_expected.pdf.0.png
new file mode 100644
index 0000000..eacc914
--- /dev/null
+++ b/testing/resources/pixel/bug_1442723_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1442723_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1442723_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..46281bc
--- /dev/null
+++ b/testing/resources/pixel/bug_1442723_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1442723_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1442723_expected_skia.pdf.0.png
new file mode 100644
index 0000000..21011a4
--- /dev/null
+++ b/testing/resources/pixel/bug_1442723_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1491_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1491_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..fdae579
--- /dev/null
+++ b/testing/resources/pixel/bug_1491_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1638_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1638_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..9d00646
--- /dev/null
+++ b/testing/resources/pixel/bug_1638_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1639_1_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1639_1_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..7c19cc3
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1752_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1752_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..ac5a641
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png
index 9cb41b8..6c129ef 100644
--- a/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1772_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1772_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..e721e2a
--- /dev/null
+++ b/testing/resources/pixel/bug_1772_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1774_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1774_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..4f4537c
--- /dev/null
+++ b/testing/resources/pixel/bug_1774_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1883_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..61c32c7
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_gdi.pdf.1.png b/testing/resources/pixel/bug_1883_expected_gdi.pdf.1.png
new file mode 100644
index 0000000..38fc46a
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_gdi.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_gdi.pdf.2.png b/testing/resources/pixel/bug_1883_expected_gdi.pdf.2.png
new file mode 100644
index 0000000..cffe10f
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_gdi.pdf.2.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_gdi.pdf.3.png b/testing/resources/pixel/bug_1883_expected_gdi.pdf.3.png
new file mode 100644
index 0000000..a2e91a3
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_gdi.pdf.3.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1922_expected.pdf.0.png b/testing/resources/pixel/bug_1922_expected.pdf.0.png
index 73057ac..2242e85 100644
--- a/testing/resources/pixel/bug_1922_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1922_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1922_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1922_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8b11ac6
--- /dev/null
+++ b/testing/resources/pixel/bug_1922_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1963_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1963_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..592b626
--- /dev/null
+++ b/testing/resources/pixel/bug_1963_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1972_2_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1972_2_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..2d0ce1d
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_2_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1983_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_1983_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..ce22301
--- /dev/null
+++ b/testing/resources/pixel/bug_1983_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_invisible_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_491_invisible_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..82bf239
--- /dev/null
+++ b/testing/resources/pixel/bug_491_invisible_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_unspecified_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_491_unspecified_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..5beb865
--- /dev/null
+++ b/testing/resources/pixel/bug_491_unspecified_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_visible_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_491_visible_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..5beb865
--- /dev/null
+++ b/testing/resources/pixel/bug_491_visible_expected_gdi.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_1_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..376f391
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_2_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_3_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_4_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_5_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..376f391
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_7_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..6c4c4ac
--- /dev/null
+++ b/testing/resources/pixel/bug_528103_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_1_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_2_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5bec703
--- /dev/null
+++ b/testing/resources/pixel/bug_551258_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_585_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_585_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..b1056a0
--- /dev/null
+++ b/testing/resources/pixel/bug_585_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_601362_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_601362_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..9288387
--- /dev/null
+++ b/testing/resources/pixel/bug_601362_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected_skia.pdf.1.png b/testing/resources/pixel/bug_632_expected_skia.pdf.1.png
index 002dcba..3e66da2 100644
--- a/testing/resources/pixel/bug_632_expected_skia.pdf.1.png
+++ b/testing/resources/pixel/bug_632_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png b/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png
index 2cd13ff..2e9c915 100644
--- a/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_714187_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_714187_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..57b1108
--- /dev/null
+++ b/testing/resources/pixel/bug_714187_expected_gdi.pdf.0.png
Binary files 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 25ad890..0cdd2cc 100644
--- a/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png
index 4a5a4b0..48a2087 100644
--- a/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725555_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_725555_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..a292669
--- /dev/null
+++ b/testing/resources/pixel/bug_725555_expected_gdi.pdf.0.png
Binary files 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 6dfa99d..7e13f31 100644
--- a/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png
Binary files 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 801b157..3cddbc0 100644
--- a/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png
Binary files 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 1b02f1b..8f668d0 100644
--- a/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736703_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736703_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f850bff
--- /dev/null
+++ b/testing/resources/pixel/bug_736703_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..9813a96
--- /dev/null
+++ b/testing/resources/pixel/bug_820345_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_843_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_843_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..47d721d
--- /dev/null
+++ b/testing/resources/pixel/bug_843_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_845697_expected_skia.pdf.0.png b/testing/resources/pixel/bug_845697_expected_skia.pdf.0.png
new file mode 100644
index 0000000..cf04e70
--- /dev/null
+++ b/testing/resources/pixel/bug_845697_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_skia.pdf.0.png b/testing/resources/pixel/bug_846_expected_skia.pdf.0.png
index f7fa9ff..10cde92 100644
--- a/testing/resources/pixel/bug_846_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/bug_846_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png
index 5635527..0f361df 100644
--- a/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png b/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png
index 5635527..0f361df 100644
--- a/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png
+++ b/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..2a06ee0
--- /dev/null
+++ b/testing/resources/pixel/bug_909762_expected_skia_linux.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_909762_expected_skia_win.pdf.0.png b/testing/resources/pixel/bug_909762_expected_skia_win.pdf.0.png
new file mode 100644
index 0000000..2a06ee0
--- /dev/null
+++ b/testing/resources/pixel/bug_909762_expected_skia_win.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..154cfee
--- /dev/null
+++ b/testing/resources/pixel/bug_925736_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_972999_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_972999_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..72da0cd
--- /dev/null
+++ b/testing/resources/pixel/bug_972999_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_983289_expected_gdi.pdf.0.png b/testing/resources/pixel/bug_983289_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..80fa56b
--- /dev/null
+++ b/testing/resources/pixel/bug_983289_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_984811_expected_skia.pdf.0.png b/testing/resources/pixel/bug_984811_expected_skia.pdf.0.png
new file mode 100644
index 0000000..9c713b5
--- /dev/null
+++ b/testing/resources/pixel/bug_984811_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..2b638ab
--- /dev/null
+++ b/testing/resources/pixel/font_size_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5e3dc05
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1_expected_skia.pdf.0.png
Binary files 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
new file mode 100644
index 0000000..5e3dc05
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/matte_expected_skia.pdf.0.png b/testing/resources/pixel/matte_expected_skia.pdf.0.png
index bdab404..97fa17e 100644
--- a/testing/resources/pixel/matte_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/matte_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/password_expected_skia.pdf.0.png b/testing/resources/pixel/password_expected_skia.pdf.0.png
index 01b4b68..c5c0b23 100644
--- a/testing/resources/pixel/password_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/password_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/rectangles_wrong_xref_size.in b/testing/resources/pixel/rectangles_wrong_xref_size.in
new file mode 100644
index 0000000..18d9ca3
--- /dev/null
+++ b/testing/resources/pixel/rectangles_wrong_xref_size.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  % Manually specify the trailer instead of using the macro to deliberately set
+  % the /Size to an incorrect value.
+  /Size 4
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/rectangles_wrong_xref_size_expected.pdf.0.png b/testing/resources/pixel/rectangles_wrong_xref_size_expected.pdf.0.png
new file mode 100644
index 0000000..ee652fa
--- /dev/null
+++ b/testing/resources/pixel/rectangles_wrong_xref_size_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/rectangles_wrong_xref_size_expected_gdi.pdf.0.png b/testing/resources/pixel/rectangles_wrong_xref_size_expected_gdi.pdf.0.png
new file mode 100644
index 0000000..72da0cd
--- /dev/null
+++ b/testing/resources/pixel/rectangles_wrong_xref_size_expected_gdi.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/rectangles_wrong_xref_size_expected_skia.pdf.0.png b/testing/resources/pixel/rectangles_wrong_xref_size_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8a6b8ed
--- /dev/null
+++ b/testing/resources/pixel/rectangles_wrong_xref_size_expected_skia.pdf.0.png
Binary files 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 d4c1f9d..348c1a5 100644
--- a/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png
Binary files 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 dd9f6a1..79ca0f5 100644
--- a/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png b/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png
index 47582b6..5f898f8 100644
--- a/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png
index 3716b9c..e144c04 100644
--- a/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png
index 3ccc1a2..da9e4b3 100644
--- a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png
index eb303b2..6adaba6 100644
--- a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png
index 072a537..b4b8c0d 100644
--- a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png
index d443a35..ab5214f 100644
--- a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png
index 884a23f..d90125f 100644
--- a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png
index 3beefe0..a2c47a3 100644
--- a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..2c2a5b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png
index 4b35140..0a9ff37 100644
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png
index fd88db2..fa7e97e 100644
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..29d3b09
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia_mac.pdf.1.png
new file mode 100644
index 0000000..77b3a7b
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia_mac.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png
index ad18d26..00bad26 100644
--- a/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png
index fe509a8..ffc1b80 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png
index e720b9b..13174e5 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png
index 384294c..52e6d96 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png
index 3366bc0..e7fb0f3 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png
index 54f04d8..a974ba2 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png
index 7691fb1..605ed4c 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png
Binary files differ
diff --git a/testing/resources/rectangles_object_zero.in b/testing/resources/rectangles_object_zero.in
new file mode 100644
index 0000000..40a45e7
--- /dev/null
+++ b/testing/resources/rectangles_object_zero.in
@@ -0,0 +1,45 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  % Deliberately references object 0.
+  /CropBox 0 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/rectangles_object_zero.pdf b/testing/resources/rectangles_object_zero.pdf
new file mode 100644
index 0000000..469ba0b
--- /dev/null
+++ b/testing/resources/rectangles_object_zero.pdf
@@ -0,0 +1,56 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  % Deliberately references object 0.
+  /CropBox 0 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000281 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+521
+%%EOF
diff --git a/testing/resources/tagged_table.in b/testing/resources/tagged_table.in
index ee298c5..6e2ddc0 100644
--- a/testing/resources/tagged_table.in
+++ b/testing/resources/tagged_table.in
@@ -203,14 +203,22 @@
   /S /TD
   /P 13 0 R
   /Pg 3 0 R
-  /A [<<
-        /O /Table
-        /ColProp (Sum)
-        /CurUSD true
-     >>]
+  /A 18 0 R
   /ID (node18)
 >>
 endobj
+{{object 18 0}} [
+  <<
+    /O /Table
+    /ColProp (Sum)
+    /CurUSD true
+    /RowSpan 19 0 R
+  >>
+]
+endobj
+{{object 19 0}}
+  3
+endobj
 {{xref}}
 {{trailer}}
 {{startxref}}
diff --git a/testing/resources/tagged_table.pdf b/testing/resources/tagged_table.pdf
index 4428682..6e2bf9e 100644
--- a/testing/resources/tagged_table.pdf
+++ b/testing/resources/tagged_table.pdf
@@ -204,16 +204,24 @@
   /S /TD
   /P 13 0 R
   /Pg 3 0 R
-  /A [<<
-        /O /Table
-        /ColProp (Sum)
-        /CurUSD true
-     >>]
+  /A 18 0 R
   /ID (node18)
 >>
 endobj
+18 0 obj [
+  <<
+    /O /Table
+    /ColProp (Sum)
+    /CurUSD true
+    /RowSpan 19 0 R
+  >>
+]
+endobj
+19 0 obj
+  3
+endobj
 xref
-0 18
+0 20
 0000000000 65535 f 
 0000000015 00000 n 
 0000000145 00000 n 
@@ -232,10 +240,12 @@
 0000002276 00000 n 
 0000002366 00000 n 
 0000002513 00000 n 
+0000002615 00000 n 
+0000002715 00000 n 
 trailer <<
   /Root 1 0 R
-  /Size 18
+  /Size 20
 >>
 startxref
-2683
+2735
 %%EOF
diff --git a/testing/resources/tagged_table_bad_parent.in b/testing/resources/tagged_table_bad_parent.in
new file mode 100644
index 0000000..8175cba
--- /dev/null
+++ b/testing/resources/tagged_table_bad_parent.in
@@ -0,0 +1,217 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 8 0 R
+  /Lang (en-US)
+  /MarkInfo <<
+    /Marked true
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Group <<
+    /CS /DeviceRGB
+    /I true
+    /S /Transparency
+  >>
+  /Resources <<
+    /ProcSet [/PDF /ImageC /ImageI /ImageB]
+    /XObject <<
+      /Tr8 5 0 R
+      /Im7 6 0 R
+    >>
+    /ExtGState <<
+      /EGS9 7 0 R
+    >>
+  >>
+  /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [-140 395 753 395.1]
+  /Group <<
+    /CS /DeviceRGB
+    /K true
+    /S /Transparency
+  >>
+  {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+{{object 7 0}} <<
+  /ca 0.5
+  /CA 0.5
+>>
+endobj
+{{object 8 0}} <<
+  /Type /StructTreeRoot
+  /ParentTree 9 0 R
+  /K [10 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Standard /P
+    /Figure /Figure
+  >>
+>>
+endobj
+{{object 9 0}} <<
+  /Nums [
+    0
+    [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R]
+  ]
+>>
+endobj
+{{object 10 0}} <<
+  /Type /StructElem
+  /S /Document
+  /K [11 0 R]
+  /P 8 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+  /Lang (en-US)
+>>
+endobj
+{{object 11 0}} <<
+  /Type /StructElem
+  /S /Table
+  /K [12 0 R] % Deliberately left off object 13 0.
+  /P 10 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /Summary ()
+      >>]
+  /ID (node12)
+  /Lang (hu)
+>>
+endobj
+{{object 12 0}} <<
+  /Type /StructElem
+  /S /TR
+  /K [14 0 R 15 0 R]
+  /P 11 0 R
+  /Pg 3 0 R
+  /ID ()
+>>
+endobj
+{{object 13 0}} <<
+  /Type /StructElem
+  /S /TR
+  /K [16 0 R] % Deliberately left off object 17 0.
+  /P 11 0 R
+  /Pg 3 0 R
+  /A <<
+      /O /Table
+     >>
+  /ID (node14)
+>>
+endobj
+{{object 14 0}} <<
+  /Type /StructElem
+  /S /TH
+  /P 12 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /Scope /Row
+      >>
+      <<
+        /O /Table
+        /ColSpan 2
+      >>]
+  /ID (node15)
+>>
+endobj
+{{object 15 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 12 0 R
+  /Pg 3 0 R
+  /ID (node16)
+>>
+endobj
+{{object 16 0}} <<
+  /Type /StructElem
+  /S /TH
+  /P 13 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /Scope /Row
+      >>]
+  /ID (node17)
+>>
+endobj
+{{object 17 0}} <<
+  /Type /StructElem
+  /S /TD
+  /P 13 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /ColProp (Sum)
+        /CurUSD true
+     >>]
+  /ID (node18)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_table_bad_parent.pdf b/testing/resources/tagged_table_bad_parent.pdf
new file mode 100644
index 0000000..43d9aa3
--- /dev/null
+++ b/testing/resources/tagged_table_bad_parent.pdf
@@ -0,0 +1,241 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /StructTreeRoot 8 0 R
+  /Lang (en-US)
+  /MarkInfo <<
+    /Marked true
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /MediaBox [0 0 612 792]
+  /Group <<
+    /CS /DeviceRGB
+    /I true
+    /S /Transparency
+  >>
+  /Resources <<
+    /ProcSet [/PDF /ImageC /ImageI /ImageB]
+    /XObject <<
+      /Tr8 5 0 R
+      /Im7 6 0 R
+    >>
+    /ExtGState <<
+      /EGS9 7 0 R
+    >>
+  >>
+  /StructParents 0
+>>
+endobj
+4 0 obj <<
+  /Length 162
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [-140 395 753 395.1]
+  /Group <<
+    /CS /DeviceRGB
+    /K true
+    /S /Transparency
+  >>
+  /Length 0
+>>
+stream
+endstream
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 61
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+7 0 obj <<
+  /ca 0.5
+  /CA 0.5
+>>
+endobj
+8 0 obj <<
+  /Type /StructTreeRoot
+  /ParentTree 9 0 R
+  /K [10 0 R]
+  /RoleMap <<
+    /Document /Document
+    /Standard /P
+    /Figure /Figure
+  >>
+>>
+endobj
+9 0 obj <<
+  /Nums [
+    0
+    [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R]
+  ]
+>>
+endobj
+10 0 obj <<
+  /Type /StructElem
+  /S /Document
+  /K [11 0 R]
+  /P 8 0 R
+  /T (TitleText)
+  /Pg 3 0 R
+  /Lang (en-US)
+>>
+endobj
+11 0 obj <<
+  /Type /StructElem
+  /S /Table
+  /K [12 0 R] % Deliberately left off object 13 0.
+  /P 10 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /Summary ()
+      >>]
+  /ID (node12)
+  /Lang (hu)
+>>
+endobj
+12 0 obj <<
+  /Type /StructElem
+  /S /TR
+  /K [14 0 R 15 0 R]
+  /P 11 0 R
+  /Pg 3 0 R
+  /ID ()
+>>
+endobj
+13 0 obj <<
+  /Type /StructElem
+  /S /TR
+  /K [16 0 R] % Deliberately left off object 17 0.
+  /P 11 0 R
+  /Pg 3 0 R
+  /A <<
+      /O /Table
+     >>
+  /ID (node14)
+>>
+endobj
+14 0 obj <<
+  /Type /StructElem
+  /S /TH
+  /P 12 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /Scope /Row
+      >>
+      <<
+        /O /Table
+        /ColSpan 2
+      >>]
+  /ID (node15)
+>>
+endobj
+15 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 12 0 R
+  /Pg 3 0 R
+  /ID (node16)
+>>
+endobj
+16 0 obj <<
+  /Type /StructElem
+  /S /TH
+  /P 13 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /Scope /Row
+      >>]
+  /ID (node17)
+>>
+endobj
+17 0 obj <<
+  /Type /StructElem
+  /S /TD
+  /P 13 0 R
+  /Pg 3 0 R
+  /A [<<
+        /O /Table
+        /ColProp (Sum)
+        /CurUSD true
+     >>]
+  /ID (node18)
+>>
+endobj
+xref
+0 18
+0000000000 65535 f 
+0000000015 00000 n 
+0000000145 00000 n 
+0000000208 00000 n 
+0000000556 00000 n 
+0000000770 00000 n 
+0000000952 00000 n 
+0000001212 00000 n 
+0000001253 00000 n 
+0000001412 00000 n 
+0000001515 00000 n 
+0000001642 00000 n 
+0000001856 00000 n 
+0000001961 00000 n 
+0000002134 00000 n 
+0000002336 00000 n 
+0000002426 00000 n 
+0000002573 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 18
+>>
+startxref
+2743
+%%EOF
diff --git a/testing/test_loader.cpp b/testing/test_loader.cpp
index aac8429..a3fad8a 100644
--- a/testing/test_loader.cpp
+++ b/testing/test_loader.cpp
@@ -6,7 +6,8 @@
 
 #include <string.h>
 
-#include "third_party/base/notreached.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/numerics/checked_math.h"
 
 TestLoader::TestLoader(pdfium::span<const char> span) : m_Span(span) {}
 
@@ -16,10 +17,9 @@
                          unsigned char* pBuf,
                          unsigned long size) {
   TestLoader* pLoader = static_cast<TestLoader*>(param);
-  if (pos + size < pos || pos + size > pLoader->m_Span.size()) {
-    NOTREACHED();
-    return 0;
-  }
+  pdfium::base::CheckedNumeric<size_t> end = pos;
+  end += size;
+  CHECK_LE(end.ValueOrDie(), pLoader->m_Span.size());
 
   memcpy(pBuf, &pLoader->m_Span[pos], size);
   return 1;
diff --git a/testing/test_loader.h b/testing/test_loader.h
index f3d0042..d0e83c5 100644
--- a/testing/test_loader.h
+++ b/testing/test_loader.h
@@ -5,7 +5,7 @@
 #ifndef TESTING_TEST_LOADER_H_
 #define TESTING_TEST_LOADER_H_
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 class TestLoader {
  public:
diff --git a/testing/tools/BUILD.gn b/testing/tools/BUILD.gn
new file mode 100644
index 0000000..8815b53
--- /dev/null
+++ b/testing/tools/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2023 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../pdfium.gni")
+
+if (pdf_is_standalone) {
+  # Generates the list of inputs required by `test_runner.py` tests.
+  action("test_runner_py") {
+    testonly = true
+
+    write_runtime_deps = "${root_out_dir}/${target_name}.runtime_deps"
+
+    sources = [ write_runtime_deps ]
+    outputs = [ "${root_out_dir}/${target_name}.json" ]
+
+    script = "generate_cas_paths.py"
+    args = [
+             "--root",
+             rebase_path("../..", root_build_dir),
+           ] + rebase_path(sources + outputs, root_build_dir)
+
+    # Unbuilt runtime dependencies.
+    data = [
+      ".",
+      "../SUPPRESSIONS",
+      "../SUPPRESSIONS_EXACT_MATCHING",
+      "../SUPPRESSIONS_IMAGE_DIFF",
+      "../corpus/",
+      "../../.vpython3",
+      "../../build/skia_gold_common/",
+      "../../build/util/lib/",
+      "../../third_party/test_fonts/",
+      "../../tools/resultdb/",
+      "../../tools/skia_goldctl/",
+    ]
+
+    # Built runtime dependencies.
+    data_deps = [
+      "../../:pdfium_diff",
+      "../../samples:pdfium_test",
+    ]
+
+    # Force `data_deps` to be built before this target, rather than in parallel.
+    deps = data_deps
+  }
+}
diff --git a/testing/tools/generate_cas_paths.py b/testing/tools/generate_cas_paths.py
new file mode 100644
index 0000000..43690eb
--- /dev/null
+++ b/testing/tools/generate_cas_paths.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright 2023 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Tool for converting GN runtime_deps to CAS archive paths."""
+
+import argparse
+from collections import deque
+import filecmp
+import json
+import logging
+from pathlib import Path
+import os
+
+EXCLUDE_DIRS = {
+    '.git',
+    '__pycache__',
+}
+
+
+def parse_runtime_deps(runtime_deps):
+  """Parses GN's `runtime_deps` format."""
+  with runtime_deps:
+    return [line.rstrip() for line in runtime_deps]
+
+
+def resolve_paths(root, initial_paths):
+  """Converts paths to CAS archive paths format."""
+  absolute_root = os.path.abspath(root)
+
+  resolved_paths = []
+  unvisited_paths = deque(map(Path, initial_paths))
+  while unvisited_paths:
+    path = unvisited_paths.popleft()
+
+    if not path.exists():
+      logging.warning('"%(path)s" does not exist', {'path': path})
+      continue
+
+    if path.is_dir():
+      # Expand specific children if any are excluded.
+      child_paths = expand_dir(path)
+      if child_paths:
+        unvisited_paths.extendleft(child_paths)
+        continue
+
+    resolved_paths.append(os.path.relpath(path, start=absolute_root))
+
+  resolved_paths.sort()
+  return [[absolute_root, path] for path in resolved_paths]
+
+
+def expand_dir(path):
+  """Explicitly expands directory if any children are excluded."""
+  expand = False
+  expanded_paths = []
+
+  for child_path in path.iterdir():
+    if child_path.name in EXCLUDE_DIRS and path.is_dir():
+      expand = True
+      continue
+    expanded_paths.append(child_path)
+
+  return expanded_paths if expand else []
+
+
+def replace_output(resolved, output_path):
+  """Atomically replaces the output with the resolved JSON if changed."""
+  new_output_path = output_path + '.new'
+  try:
+    with open(new_output_path, 'w', encoding='ascii') as new_output:
+      json.dump(resolved, new_output)
+
+    if (os.path.exists(output_path) and
+        filecmp.cmp(new_output_path, output_path, shallow=False)):
+      return
+
+    os.replace(new_output_path, output_path)
+    new_output_path = None
+  finally:
+    if new_output_path:
+      os.remove(new_output_path)
+
+
+def main():
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--root')
+  parser.add_argument(
+      'runtime_deps',
+      help='runtime_deps written by GN',
+      type=argparse.FileType('r', encoding='utf_8'),
+      metavar='input.runtime_deps')
+  parser.add_argument(
+      'output_json',
+      help='CAS archive paths in JSON format',
+      metavar='output.json')
+  args = parser.parse_args()
+
+  runtime_deps = parse_runtime_deps(args.runtime_deps)
+  resolved_paths = resolve_paths(args.root, runtime_deps)
+  replace_output(resolved_paths, args.output_json)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/testing/tools/pngdiffer.py b/testing/tools/pngdiffer.py
index 2044a34..ceb4022 100755
--- a/testing/tools/pngdiffer.py
+++ b/testing/tools/pngdiffer.py
@@ -15,8 +15,11 @@
 
 _PNG_OPTIMIZER = 'optipng'
 
+# Each suffix order acts like a path along a tree, with the leaves being the
+# most specific, and the root being the least specific.
 _COMMON_SUFFIX_ORDER = ('_{os}', '')
 _AGG_SUFFIX_ORDER = ('_agg_{os}', '_agg') + _COMMON_SUFFIX_ORDER
+_GDI_SUFFIX_ORDER = ('_gdi_{os}', '_gdi') + _COMMON_SUFFIX_ORDER
 _SKIA_SUFFIX_ORDER = ('_skia_{os}', '_skia') + _COMMON_SUFFIX_ORDER
 
 
@@ -37,14 +40,19 @@
 
 class PNGDiffer():
 
-  def __init__(self, finder, features, reverse_byte_order):
+  def __init__(self, finder, reverse_byte_order, rendering_option):
     self.pdfium_diff_path = finder.ExecutablePath('pdfium_diff')
     self.os_name = finder.os_name
     self.reverse_byte_order = reverse_byte_order
-    if 'SKIA' in features:
+
+    if rendering_option == 'agg':
+      self.suffix_order = _AGG_SUFFIX_ORDER
+    elif rendering_option == 'gdi':
+      self.suffix_order = _GDI_SUFFIX_ORDER
+    elif rendering_option == 'skia':
       self.suffix_order = _SKIA_SUFFIX_ORDER
     else:
-      self.suffix_order = _AGG_SUFFIX_ORDER
+      raise ValueError(f'rendering_option={rendering_option}')
 
   def CheckMissingTools(self, regenerate_expected):
     if regenerate_expected and not shutil.which(_PNG_OPTIMIZER):
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_properties.py b/testing/tools/skia_gold/pdfium_skia_gold_properties.py
index aeb8ef9..874e643 100644
--- a/testing/tools/skia_gold/pdfium_skia_gold_properties.py
+++ b/testing/tools/skia_gold/pdfium_skia_gold_properties.py
@@ -3,25 +3,12 @@
 # found in the LICENSE file.
 """PDFium implementation of //build/skia_gold_common/skia_gold_properties.py."""
 
-import subprocess
-import sys
-
 import pdfium_root
 from skia_gold_common import skia_gold_properties
 
 
 class PDFiumSkiaGoldProperties(skia_gold_properties.SkiaGoldProperties):
 
-  @staticmethod
-  def _GetGitOriginMainHeadSha1():
+  def _GetGitRepoDirectory(self):
     root_finder = pdfium_root.RootDirectoryFinder()
-    try:
-      return subprocess.check_output(['git', 'rev-parse', 'origin/main'],
-                                     shell=_IsWin(),
-                                     cwd=root_finder.pdfium_root).strip()
-    except subprocess.CalledProcessError:
-      return None
-
-
-def _IsWin():
-  return sys.platform == 'win32'
+    return root_finder.pdfium_root
diff --git a/testing/tools/skia_gold/skia_gold.py b/testing/tools/skia_gold/skia_gold.py
index e3595d2..9eb9871 100644
--- a/testing/tools/skia_gold/skia_gold.py
+++ b/testing/tools/skia_gold/skia_gold.py
@@ -112,7 +112,6 @@
                                       process_name)
       clear_gold_output_dir(self._output_dir)
     self._keys = _ParseKeyValuePairs(skia_gold_args.gold_key)
-    self._old_gold_props = _ParseKeyValuePairs(skia_gold_args.gold_properties)
     self._skia_gold_args = skia_gold_args
     self._skia_gold_session_manager = None
     self._skia_gold_properties = None
@@ -129,13 +128,6 @@
 
   def GetSkiaGoldProperties(self):
     if not self._skia_gold_properties:
-      if self._old_gold_props:
-        self._skia_gold_args.git_revision = self._old_gold_props['gitHash']
-        self._skia_gold_args.gerrit_issue = self._old_gold_props['issue']
-        self._skia_gold_args.gerrit_patchset = self._old_gold_props['patchset']
-        self._skia_gold_args.buildbucket_id = \
-            self._old_gold_props['buildbucket_build_id']
-
       if self._skia_gold_args.local_pixel_tests is None:
         self._skia_gold_args.local_pixel_tests = 'SWARMING_SERVER' \
             not in os.environ
diff --git a/testing/tools/suppressor.py b/testing/tools/suppressor.py
index 989f4dd..6fea735 100755
--- a/testing/tools/suppressor.py
+++ b/testing/tools/suppressor.py
@@ -11,10 +11,11 @@
 
 class Suppressor:
 
-  def __init__(self, finder, features, js_disabled, xfa_disabled):
+  def __init__(self, finder, features, js_disabled, xfa_disabled,
+               rendering_option):
     self.has_v8 = not js_disabled and 'V8' in features
     self.has_xfa = not js_disabled and not xfa_disabled and 'XFA' in features
-    self.has_skia = 'SKIA' in features
+    self.rendering_option = rendering_option
     self.suppression_set = self._LoadSuppressedSet('SUPPRESSIONS', finder)
     self.image_suppression_set = self._LoadSuppressedSet(
         'SUPPRESSIONS_IMAGE_DIFF', finder)
@@ -24,11 +25,10 @@
   def _LoadSuppressedSet(self, suppressions_filename, finder):
     v8_option = "v8" if self.has_v8 else "nov8"
     xfa_option = "xfa" if self.has_xfa else "noxfa"
-    rendering_option = "skia" if self.has_skia else "agg"
     with open(os.path.join(finder.TestingDir(), suppressions_filename)) as f:
       return set(
-          self._FilterSuppressions(common.os_name(), v8_option,
-                                   xfa_option, rendering_option,
+          self._FilterSuppressions(common.os_name(), v8_option, xfa_option,
+                                   self.rendering_option,
                                    self._ExtractSuppressions(f)))
 
   def _ExtractSuppressions(self, f):
diff --git a/testing/tools/test_runner.py b/testing/tools/test_runner.py
index ad348aa..c7f8aa7 100644
--- a/testing/tools/test_runner.py
+++ b/testing/tools/test_runner.py
@@ -93,8 +93,7 @@
       if test_result.reason:
         print(f'Failure reason: {test_result.reason}')
       if test_result.log:
-        decoded_log = bytes.decode(test_result.log, errors='backslashreplace')
-        print(f'Test output:\n{decoded_log}')
+        print(f'Test output:\n{test_result.log}')
       for artifact in test_result.image_artifacts:
         if artifact.skia_gold_status == result_types.FAIL:
           print(f'Failed Skia Gold: {artifact.image_path}')
@@ -200,19 +199,6 @@
         default=False,
         help='When flag is on, skia gold tests will be run.')
 
-    # TODO: Remove when pdfium recipe stops passing this argument
-    parser.add_argument(
-        '--gold_properties',
-        default='',
-        help='Key value pairs that are written to the top level of the JSON '
-        'file that is ingested by Gold.')
-
-    # TODO: Remove when pdfium recipe stops passing this argument
-    parser.add_argument(
-        '--gold_ignore_hashes',
-        default='',
-        help='Path to a file with MD5 hashes we wish to ignore.')
-
     parser.add_argument(
         '--regenerate_expected',
         action='store_true',
@@ -235,6 +221,11 @@
         'when image comparison fails.')
 
     parser.add_argument(
+        '--use-renderer',
+        choices=('agg', 'gdi', 'skia'),
+        help='Forces the renderer to use.')
+
+    parser.add_argument(
         'inputted_file_paths',
         nargs='*',
         help='Path to test files to run, relative to '
@@ -252,7 +243,11 @@
       print(f"FAILURE: Can't find test executable '{pdfium_test_path}'")
       print('Use --build-dir to specify its location.')
       return 1
-    self.per_process_config.InitializeFeatures(pdfium_test_path)
+
+    error_message = self.per_process_config.InitializeFeatures(pdfium_test_path)
+    if error_message:
+      print('FAILURE:', error_message)
+      return 1
 
     self.per_process_state = _PerProcessState(self.per_process_config)
     shutil.rmtree(self.per_process_state.working_dir, ignore_errors=True)
@@ -431,14 +426,16 @@
     delete_output_on_success: Whether to delete output on success.
     enforce_expected_images: Whether to enforce expected images.
     options: The dictionary of command line options.
-    features: The list of features supported by `pdfium_test`.
+    features: The set of features supported by `pdfium_test`.
+    rendering_option: The renderer to use (agg, gdi, or skia).
   """
   test_dir: str
   test_type: str
   delete_output_on_success: bool = False
   enforce_expected_images: bool = False
   options: dict = None
-  features: list = None
+  features: set = None
+  rendering_option: str = None
 
   def NewFinder(self):
     return common.DirectoryFinder(self.options.build_dir)
@@ -449,7 +446,25 @@
   def InitializeFeatures(self, pdfium_test_path):
     output = subprocess.check_output([pdfium_test_path, '--show-config'],
                                      timeout=TEST_TIMEOUT)
-    self.features = output.decode('utf-8').strip().split(',')
+    self.features = set(output.decode('utf-8').strip().split(','))
+
+    if 'SKIA' in self.features:
+      self.rendering_option = 'skia'
+    else:
+      self.rendering_option = 'agg'
+
+    if self.options.use_renderer == 'agg':
+      self.rendering_option = 'agg'
+    elif self.options.use_renderer == 'gdi':
+      if 'GDI' not in self.features:
+        return 'pdfium_test does not support the GDI renderer'
+      self.rendering_option = 'gdi'
+    elif self.options.use_renderer == 'skia':
+      if 'SKIA' not in self.features:
+        return 'pdfium_test does not support the Skia renderer'
+      self.rendering_option = 'skia'
+
+    return None
 
 
 class _PerProcessState:
@@ -475,9 +490,10 @@
 
     self.test_suppressor = suppressor.Suppressor(
         finder, self.features, self.options.disable_javascript,
-        self.options.disable_xfa)
-    self.image_differ = pngdiffer.PNGDiffer(finder, self.features,
-                                            self.options.reverse_byte_order)
+        self.options.disable_xfa, config.rendering_option)
+    self.image_differ = pngdiffer.PNGDiffer(finder,
+                                            self.options.reverse_byte_order,
+                                            config.rendering_option)
 
     self.process_name = multiprocessing.current_process().name
     self.skia_tester = None
@@ -598,6 +614,7 @@
         test_result.log = run_result.stdout
       else:
         test_result.log = run_result.stderr
+      test_result.log = test_result.log.decode(errors='backslashreplace')
     return test_result
 
   def GenerateAndTest(self, test_function):
@@ -678,7 +695,9 @@
 
     if txt_data:
       return self.test_case.NewResult(
-          result_types.FAIL, log=txt_data, reason=f'{txt_path} should be empty')
+          result_types.FAIL,
+          log=txt_data.decode(errors='backslashreplace'),
+          reason=f'{txt_path} should be empty')
 
     return self.test_case.NewResult(result_types.PASS)
 
@@ -714,6 +733,9 @@
     if self.options.reverse_byte_order:
       cmd_to_run.append('--reverse-byte-order')
 
+    if self.options.use_renderer:
+      cmd_to_run.append(f'--use-renderer={self.options.use_renderer}')
+
     cmd_to_run.append(self.pdf_path)
 
     with BytesIO() as command_output:
@@ -754,7 +776,7 @@
 
         for artifact in test_result.image_artifacts:
           artifact.image_diff = diff_map.get(artifact.image_path)
-        test_result.log = ''.join(diff_log).encode()
+        test_result.log = ''.join(diff_log)
 
     elif _per_process_state.enforce_expected_images:
       if not self.IsImageDiffSuppressed():
diff --git a/testing/unit_test_main.cpp b/testing/unit_test_main.cpp
index 16e8f0f..28476b0 100644
--- a/testing/unit_test_main.cpp
+++ b/testing/unit_test_main.cpp
@@ -7,6 +7,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/pdf_test_environment.h"
 
+#if defined(PDF_USE_PARTITION_ALLOC)
+#include "testing/allocator_shim_config.h"
+#endif
+
 #ifdef PDF_ENABLE_V8
 #include "testing/v8_test_environment.h"
 #ifdef PDF_ENABLE_XFA
@@ -17,6 +21,10 @@
 // Can't use gtest-provided main since we need to initialize partition
 // alloc before invoking any test, and add test environments.
 int main(int argc, char** argv) {
+#if defined(PDF_USE_PARTITION_ALLOC)
+  pdfium::ConfigurePartitionAllocShimPartitionForTest();
+#endif  // defined(PDF_USE_PARTITION_ALLOC)
+
   FX_InitializeMemoryAllocators();
 
   // PDF test environment will be deleted by gtest.
diff --git a/testing/utils/hash.h b/testing/utils/hash.h
index c2164e8..e508993 100644
--- a/testing/utils/hash.h
+++ b/testing/utils/hash.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 
 std::string CryptToBase16(const uint8_t* digest);
 std::string GenerateMD5Base16(pdfium::span<const uint8_t> data);
diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn
index 534b3b8..3683ab8 100644
--- a/third_party/BUILD.gn
+++ b/third_party/BUILD.gn
@@ -8,16 +8,6 @@
 import("//build_overrides/build.gni")
 import("../pdfium.gni")
 
-group("third_party") {
-  deps = [
-    ":pdfium_base",
-    ":skia_shared",
-  ]
-  if (pdf_bundle_freetype) {
-    deps += [ ":fx_freetype" ]
-  }
-}
-
 config("pdfium_third_party_config") {
   configs = [
     "..:pdfium_common_config",
@@ -218,7 +208,7 @@
 }
 
 config("fx_agg_warnings") {
-  visibility = [ ":*" ]
+  visibility = [ ":fx_agg" ]
   if (is_clang) {
     # calc_butt_cap() in agg_vcgen_stroke.cpp is unused.
     cflags = [ "-Wno-unused-function" ]
@@ -234,21 +224,33 @@
     ":fx_agg_warnings",
   ]
   sources = [
+    "agg23/agg_array.h",
     "agg23/agg_basics.h",
     "agg23/agg_clip_liang_barsky.h",
+    "agg23/agg_color_gray.h",
+    "agg23/agg_conv_adaptor_vcgen.h",
     "agg23/agg_conv_dash.h",
     "agg23/agg_conv_stroke.h",
     "agg23/agg_curves.cpp",
     "agg23/agg_curves.h",
+    "agg23/agg_math.h",
+    "agg23/agg_math_stroke.h",
     "agg23/agg_path_storage.cpp",
     "agg23/agg_path_storage.h",
+    "agg23/agg_pixfmt_gray.h",
     "agg23/agg_rasterizer_scanline_aa.cpp",
     "agg23/agg_rasterizer_scanline_aa.h",
+    "agg23/agg_render_scanlines.h",
+    "agg23/agg_renderer_base.h",
     "agg23/agg_renderer_scanline.h",
     "agg23/agg_rendering_buffer.h",
     "agg23/agg_scanline_u.h",
+    "agg23/agg_shorten_path.h",
     "agg23/agg_vcgen_dash.cpp",
+    "agg23/agg_vcgen_dash.h",
     "agg23/agg_vcgen_stroke.cpp",
+    "agg23/agg_vcgen_stroke.h",
+    "agg23/agg_vertex_sequence.h",
   ]
   deps = [ "../core/fxcrt" ]
 }
@@ -546,12 +548,13 @@
     "base/component_export.h",
     "base/containers/adapters.h",
     "base/containers/contains.h",
-    "base/cxx17_backports.h",
+    "base/containers/span.h",
     "base/debug/alias.cc",
     "base/debug/alias.h",
     "base/immediate_crash.h",
     "base/memory/aligned_memory.cc",
     "base/memory/aligned_memory.h",
+    "base/memory/ptr_util.h",
     "base/no_destructor.h",
     "base/notreached.h",
     "base/numerics/checked_math.h",
@@ -565,7 +568,6 @@
     "base/numerics/safe_math_arm_impl.h",
     "base/numerics/safe_math_clang_gcc_impl.h",
     "base/numerics/safe_math_shared_impl.h",
-    "base/span.h",
     "base/sys_byteorder.h",
     "base/template_util.h",
   ]
@@ -599,14 +601,3 @@
     deps += [ "//testing/gtest" ]
   }
 }
-
-source_set("skia_shared") {
-  sources = [
-    "skia_shared/SkFloatToDecimal.cpp",
-    "skia_shared/SkFloatToDecimal.h",
-  ]
-  configs += [
-    "../:pdfium_strict_config",
-    "../:pdfium_noshorten_config",
-  ]
-}
diff --git a/third_party/NotoSansCJK/README.pdfium b/third_party/NotoSansCJK/README.pdfium
index 80b1d09..5c8ba5a 100644
--- a/third_party/NotoSansCJK/README.pdfium
+++ b/third_party/NotoSansCJK/README.pdfium
@@ -4,6 +4,7 @@
 License: SIL Open Font License v1.1
 License File: LICENSE
 Security Critical: no
+Shipped: no
 
 Description:
 NotoSansSC-Regular.subset.otf contains a subset of NotoSansSC-Regular font.
diff --git a/third_party/agg23/0015-include-string-h.patch b/third_party/agg23/0015-include-string-h.patch
new file mode 100644
index 0000000..dcbec56
--- /dev/null
+++ b/third_party/agg23/0015-include-string-h.patch
@@ -0,0 +1,26 @@
+diff --git a/third_party/agg23/agg_array.h b/third_party/agg23/agg_array.h
+index b82d95296..ed22c40e9 100644
+--- a/third_party/agg23/agg_array.h
++++ b/third_party/agg23/agg_array.h
+@@ -16,6 +16,8 @@
+ #ifndef AGG_ARRAY_INCLUDED
+ #define AGG_ARRAY_INCLUDED
+ 
++#include <string.h>
++
+ #include "agg_basics.h"
+ #include "core/fxcrt/fx_memory.h"  // For FXSYS_* macros.
+ 
+diff --git a/third_party/agg23/agg_path_storage.cpp b/third_party/agg23/agg_path_storage.cpp
+index 2981e9c0c..d4225124d 100644
+--- a/third_party/agg23/agg_path_storage.cpp
++++ b/third_party/agg23/agg_path_storage.cpp
+@@ -25,6 +25,8 @@
+ 
+ #include "agg_path_storage.h"
+ 
++#include <string.h>
++
+ #include "agg_math.h"
+ #include "core/fxcrt/fx_memory.h"
+ 
diff --git a/third_party/agg23/README.pdfium b/third_party/agg23/README.pdfium
index 1b83932..b8286cf 100644
--- a/third_party/agg23/README.pdfium
+++ b/third_party/agg23/README.pdfium
@@ -3,6 +3,7 @@
 Version: 2.3
 Security Critical: yes
 License: MIT
+Shipped: yes
 
 Description:
 2D vector graphics library.
@@ -32,3 +33,4 @@
 0013-cxx20.patch: C++20 support.
 0014-ubsan-render-line.patch: Fix some integer overflows in
 outline_aa::render_line().
+0015-include-string-h.patch: IWYU for <string.h>
\ No newline at end of file
diff --git a/third_party/agg23/agg_array.h b/third_party/agg23/agg_array.h
index b82d952..ed22c40 100644
--- a/third_party/agg23/agg_array.h
+++ b/third_party/agg23/agg_array.h
@@ -16,6 +16,8 @@
 #ifndef AGG_ARRAY_INCLUDED
 #define AGG_ARRAY_INCLUDED
 
+#include <string.h>
+
 #include "agg_basics.h"
 #include "core/fxcrt/fx_memory.h"  // For FXSYS_* macros.
 
diff --git a/third_party/agg23/agg_path_storage.cpp b/third_party/agg23/agg_path_storage.cpp
index 2981e9c..d422512 100644
--- a/third_party/agg23/agg_path_storage.cpp
+++ b/third_party/agg23/agg_path_storage.cpp
@@ -25,6 +25,8 @@
 
 #include "agg_path_storage.h"
 
+#include <string.h>
+
 #include "agg_math.h"
 #include "core/fxcrt/fx_memory.h"
 
diff --git a/third_party/android_sdk/BUILD.gn b/third_party/android_sdk/BUILD.gn
deleted file mode 100644
index da970c3..0000000
--- a/third_party/android_sdk/BUILD.gn
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-
-config("cpu_features_include") {
-  include_dirs = [ "$android_ndk_root/sources/android/cpufeatures" ]
-}
-
-config("cpu_features_warnings") {
-  if (is_clang) {
-    # cpu-features.c has few unused functions on x86 b/26403333
-    cflags = [ "-Wno-unused-function" ]
-  }
-}
-
-source_set("cpu_features") {
-  sources = [ "$android_ndk_root/sources/android/cpufeatures/cpu-features.c" ]
-  public_configs = [ ":cpu_features_include" ]
-
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [
-    "//build/config/compiler:no_chromium_code",
-
-    # Must be after no_chromium_code for warning flags to be ordered correctly.
-    ":cpu_features_warnings",
-  ]
-}
diff --git a/third_party/base/span.h b/third_party/base/containers/span.h
similarity index 93%
rename from third_party/base/span.h
rename to third_party/base/containers/span.h
index cc980ba..660d92c 100644
--- a/third_party/base/span.h
+++ b/third_party/base/containers/span.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BASE_SPAN_H_
-#define THIRD_PARTY_BASE_SPAN_H_
+#ifndef THIRD_PARTY_BASE_CONTAINERS_SPAN_H_
+#define THIRD_PARTY_BASE_CONTAINERS_SPAN_H_
 
 #include <stddef.h>
 
@@ -200,16 +200,28 @@
   constexpr span(std::array<T, N>& array) noexcept : span(array.data(), N) {}
 
   // Conversion from a container that provides |T* data()| and |integral_type
-  // size()|.
+  // size()|. Note that |data()| may not return nullptr for some empty
+  // containers, which can lead to container overflow errors when probing
+  // unowned ptrs.
+#if defined(ADDRESS_SANITIZER) && defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
+  template <typename Container,
+            typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
+  constexpr span(Container& container)
+      : span(container.size() ? container.data() : nullptr, container.size()) {}
+#else
   template <typename Container,
             typename = internal::EnableIfSpanCompatibleContainer<Container, T>>
   constexpr span(Container& container)
       : span(container.data(), container.size()) {}
+#endif
+
   template <
       typename Container,
       typename = internal::EnableIfConstSpanCompatibleContainer<Container, T>>
   span(const Container& container) : span(container.data(), container.size()) {}
+
   constexpr span(const span& other) noexcept = default;
+
   // Conversions from spans of compatible types: this allows a span<T> to be
   // seamlessly used as a span<const T>, but not the other way around.
   template <typename U, typename = internal::EnableIfLegalSpanConversion<U, T>>
@@ -288,13 +300,19 @@
 
  private:
   void ReleaseEmptySpan() noexcept {
+#if defined(ADDRESS_SANITIZER) && !defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
     // Empty spans might point to byte N+1 of a N-byte object, legal for
     // C pointers but not UnownedPtrs.
     if (!size_)
       data_.ReleaseBadPointer();
+#endif
   }
 
+#if defined(UNOWNED_PTR_IS_BASE_RAW_PTR)
+  raw_ptr<T, AllowPtrArithmetic> data_ = nullptr;
+#else
   UnownedPtr<T> data_;
+#endif
   size_t size_;
 };
 
@@ -377,4 +395,4 @@
 
 }  // namespace pdfium
 
-#endif  // THIRD_PARTY_BASE_SPAN_H_
+#endif  // THIRD_PARTY_BASE_CONTAINERS_SPAN_H_
diff --git a/third_party/base/cxx17_backports.h b/third_party/base/cxx17_backports.h
deleted file mode 100644
index 0672331..0000000
--- a/third_party/base/cxx17_backports.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BASE_CXX17_BACKPORTS_H_
-#define THIRD_PARTY_BASE_CXX17_BACKPORTS_H_
-
-#include <stddef.h>
-
-#include <functional>
-
-#include "third_party/base/check.h"
-
-namespace pdfium {
-
-// C++14 implementation of C++17's std::clamp():
-// https://en.cppreference.com/w/cpp/algorithm/clamp
-// Please note that the C++ spec makes it undefined behavior to call std::clamp
-// with a value of `lo` that compares greater than the value of `hi`. This
-// implementation uses a CHECK to enforce this as a hard restriction.
-template <typename T, typename Compare>
-constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) {
-  CHECK(!comp(hi, lo));
-  return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
-}
-
-template <typename T>
-constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
-  return pdfium::clamp(v, lo, hi, std::less<T>{});
-}
-
-}  // namespace pdfium
-
-#endif  // THIRD_PARTY_BASE_CXX17_BACKPORTS_H_
diff --git a/third_party/base/ptr_util.h b/third_party/base/memory/ptr_util.h
similarity index 79%
rename from third_party/base/ptr_util.h
rename to third_party/base/memory/ptr_util.h
index 3be55b4..c9df831 100644
--- a/third_party/base/ptr_util.h
+++ b/third_party/base/memory/ptr_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BASE_PTR_UTIL_H_
-#define THIRD_PARTY_BASE_PTR_UTIL_H_
+#ifndef THIRD_PARTY_BASE_MEMORY_PTR_UTIL_H_
+#define THIRD_PARTY_BASE_MEMORY_PTR_UTIL_H_
 
 #include <memory>
 
@@ -19,4 +19,4 @@
 
 }  // namespace pdfium
 
-#endif  // THIRD_PARTY_BASE_PTR_UTIL_H_
+#endif  // THIRD_PARTY_BASE_MEMORY_PTR_UTIL_H_
diff --git a/third_party/base/numerics/README.pdfium b/third_party/base/numerics/README.pdfium
index 8d73ef3..c1ad6be 100644
--- a/third_party/base/numerics/README.pdfium
+++ b/third_party/base/numerics/README.pdfium
@@ -1,3 +1,5 @@
+Shipped: yes
+
 Description:
 Chromium's safe numerics library
 
diff --git a/third_party/cpu_features/BUILD.gn b/third_party/cpu_features/BUILD.gn
new file mode 100644
index 0000000..8f1c8dc
--- /dev/null
+++ b/third_party/cpu_features/BUILD.gn
@@ -0,0 +1,61 @@
+# Copyright 2023 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("cpu_features_config") {
+  cflags = [ "-Wno-unused-function" ]
+  defines = [
+    "STACK_LINE_READER_BUFFER_SIZE=1024",
+    "HAVE_STRONG_GETAUXVAL",
+  ]
+  include_dirs = [ "src/include" ]
+}
+
+config("ndk_compat_headers") {
+  include_dirs = [ "src/ndk_compat" ]
+}
+
+source_set("cpuinfo") {
+  sources = [
+    "src/src/copy.inl",
+    "src/src/define_introspection.inl",
+    "src/src/define_introspection_and_hwcaps.inl",
+    "src/src/equals.inl",
+    "src/src/filesystem.c",
+    "src/src/hwcaps.c",
+    "src/src/stack_line_reader.c",
+    "src/src/string_view.c",
+  ]
+  if (current_cpu == "x86" || current_cpu == "x64") {
+    sources += [
+      "src/src/impl_x86__base_implementation.inl",
+      "src/src/impl_x86_freebsd.c",
+      "src/src/impl_x86_linux_or_android.c",
+      "src/src/impl_x86_macos.c",
+      "src/src/impl_x86_windows.c",
+    ]
+  } else if (current_cpu == "arm") {
+    sources += [ "src/src/impl_arm_linux_or_android.c" ]
+  } else if (current_cpu == "arm64") {
+    sources += [ "src/src/impl_aarch64_linux_or_android.c" ]
+  } else if (current_cpu == "mips") {
+    sources += [ "src/src/impl_mips_linux_or_android.c" ]
+  } else if (current_cpu == "ppc") {
+    sources += [ "src/src/impl_ppc_linux.c" ]
+  } else if (current_cpu == "riscv64") {
+    sources += [ "src/src/impl_riscv_linux.c" ]
+  } else {
+    error("Missing definition for architecture: $current_cpu")
+  }
+  configs += [ ":cpu_features_config" ]
+}
+
+source_set("ndk_compat") {
+  sources = [
+    "src/ndk_compat/cpu-features.c",
+    "src/ndk_compat/cpu-features.h",
+  ]
+  configs += [ ":cpu_features_config" ]
+  public_configs = [ ":ndk_compat_headers" ]
+  deps = [ ":cpuinfo" ]
+}
diff --git a/third_party/cpu_features/README.pdfium b/third_party/cpu_features/README.pdfium
new file mode 100644
index 0000000..a0c12d6
--- /dev/null
+++ b/third_party/cpu_features/README.pdfium
@@ -0,0 +1,15 @@
+Name: cpu_features
+Short Name: cpu_features
+URL: https://github.com/google/cpu_features
+Version: v0.8.0
+Date: 2023/05/17
+License: Apache 2.0
+License File: src/LICENSE
+Security Critical: yes
+Shipped: no
+
+Description:
+cpu_features is a library to retrieve CPU features at runtime. It is used to
+make decisions about performance optimization and features a drop-in replacement
+for Android's cpu-features.h. It is Android's recommended replacement for libraries
+that utilize cpu-features.h.
diff --git a/third_party/freetype/README.pdfium b/third_party/freetype/README.pdfium
index 24ea087..22fdfc9 100644
--- a/third_party/freetype/README.pdfium
+++ b/third_party/freetype/README.pdfium
@@ -1,9 +1,10 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-13-0-65
-Revision: b0a4f99278aa7e14bd1d0d9e40ad28dce543fde6
-CPEPrefix: cpe:/a:freetype:freetype:2.13.0
+Version: VER-2-13-1-15
+Revision: b2584c738f1a92e6369890cff0504cc044315b38
+CPEPrefix: cpe:/a:freetype:freetype:2.13.1
 Security Critical: yes
+Shipped: yes
 License: FreeType License (FTL)
 License File: FTL.TXT
 
diff --git a/third_party/fuchsia-sdk/README.chromium b/third_party/fuchsia-sdk/README.chromium
index 613c974..4bdbe99 100644
--- a/third_party/fuchsia-sdk/README.chromium
+++ b/third_party/fuchsia-sdk/README.chromium
@@ -2,6 +2,7 @@
 URL: https://fuchsia.dev/fuchsia-src/development/sdk/download
 Version: 0
 Security Critical: yes
+Shipped: yes
 License: BSD 3-Clause, Apache 2.0, MIT
 License File: sdk/LICENSE
 
diff --git a/third_party/googletest/BUILD.gn b/third_party/googletest/BUILD.gn
index 02f0ff8..fb7b182 100644
--- a/third_party/googletest/BUILD.gn
+++ b/third_party/googletest/BUILD.gn
@@ -61,6 +61,7 @@
 source_set("gtest") {
   testonly = true
   sources = [
+    "custom/gtest/internal/custom/gtest-printers.h",
     "custom/gtest/internal/custom/gtest.h",
     "custom/gtest/internal/custom/stack_trace_getter.cc",
     "custom/gtest/internal/custom/stack_trace_getter.h",
@@ -127,7 +128,6 @@
   ]
 
   deps = []
-  public_deps = []
 
   if (is_fuchsia) {
     deps += [
diff --git a/third_party/googletest/README.pdfium b/third_party/googletest/README.pdfium
index bd2d36a..f3ea2de 100644
--- a/third_party/googletest/README.pdfium
+++ b/third_party/googletest/README.pdfium
@@ -3,7 +3,7 @@
 URL: https://github.com/google/googletest.git
 Version: 1.8.0.git-a45c24ac1878932e0dc5fbc0d78a699befd386d3
 License: BSD
-License File: NOT_SHIPPED
+Shipped: no
 Security critical: no
 
 Google Test is imported as-is, to facilitate version bumping. However, the
diff --git a/third_party/googletest/custom/gtest/internal/custom/gtest-printers.h b/third_party/googletest/custom/gtest/internal/custom/gtest-printers.h
new file mode 100644
index 0000000..523dc14
--- /dev/null
+++ b/third_party/googletest/custom/gtest/internal/custom/gtest-printers.h
@@ -0,0 +1,35 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#define THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+
+#include <string>
+
+namespace fxcrt {
+class ByteString;
+}
+
+namespace testing {
+
+// If a C string is compared with a PDFium string object, then it is meant to
+// point to a NUL-terminated string, and thus print it as a string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
+  template <>                                                            \
+  class internal::FormatForComparison<CharType*, OtherStringType> {      \
+   public:                                                               \
+    static std::string Format(CharType* value) {                         \
+      return ::testing::PrintToString(value);                            \
+    }                                                                    \
+  }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, fxcrt::ByteString);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, fxcrt::ByteString);
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
+
+}  // namespace testing
+
+#endif  // THIRD_PARTY_GOOGLETEST_CUSTOM_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
diff --git a/third_party/lcms/0035-func-ptr-mixup.patch b/third_party/lcms/0035-func-ptr-mixup.patch
new file mode 100644
index 0000000..8232e21
--- /dev/null
+++ b/third_party/lcms/0035-func-ptr-mixup.patch
@@ -0,0 +1,29 @@
+diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
+index e3212fb4d..a5475709b 100644
+--- a/third_party/lcms/src/cmsopt.c
++++ b/third_party/lcms/src/cmsopt.c
+@@ -100,6 +100,15 @@ typedef struct {
+ 
+ } Curves16Data;
+ 
++// A simple adapter to prevent _cmsPipelineEval16Fn vs. _cmsInterpFn16
++// confusion, which trips up UBSAN.
++static
++void Lerp16Adapter(CMSREGISTER const cmsUInt16Number in[],
++                   CMSREGISTER cmsUInt16Number out[],
++                   const void* data) {
++    cmsInterpParams* params = (cmsInterpParams*)data;
++    params->Interpolation.Lerp16(in, out, params);
++}
+ 
+ // Simple optimizations ----------------------------------------------------------------------------------------------------------
+ 
+@@ -805,7 +814,7 @@ Error:
+ 
+     if (DataSetIn == NULL && DataSetOut == NULL) {
+ 
+-        _cmsPipelineSetOptimizationParameters(Dest, (_cmsPipelineEval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL);
++        _cmsPipelineSetOptimizationParameters(Dest, Lerp16Adapter, DataCLUT->Params, NULL, NULL);
+     }
+     else {
+ 
diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium
index b71c6f2..ad93835 100644
--- a/third_party/lcms/README.pdfium
+++ b/third_party/lcms/README.pdfium
@@ -2,6 +2,7 @@
 URL: http://www.littlecms.com/
 Version: 2.15
 Security Critical: yes
+Shipped: yes
 License: MIT License
 
 Description:
@@ -20,3 +21,4 @@
 0030-const-data.patch: Mark many data structures as const.
 0033-opt-integer-overflow.patch: Protect against integer overflow.
 0034-dead-code.patch: Remove dead code.
+0035-func-ptr-mixup.patch: Prevent mixing up function pointer types.
diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
index e3212fb..a547570 100644
--- a/third_party/lcms/src/cmsopt.c
+++ b/third_party/lcms/src/cmsopt.c
@@ -100,6 +100,15 @@
 
 } Curves16Data;
 
+// A simple adapter to prevent _cmsPipelineEval16Fn vs. _cmsInterpFn16
+// confusion, which trips up UBSAN.
+static
+void Lerp16Adapter(CMSREGISTER const cmsUInt16Number in[],
+                   CMSREGISTER cmsUInt16Number out[],
+                   const void* data) {
+    cmsInterpParams* params = (cmsInterpParams*)data;
+    params->Interpolation.Lerp16(in, out, params);
+}
 
 // Simple optimizations ----------------------------------------------------------------------------------------------------------
 
@@ -805,7 +814,7 @@
 
     if (DataSetIn == NULL && DataSetOut == NULL) {
 
-        _cmsPipelineSetOptimizationParameters(Dest, (_cmsPipelineEval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL);
+        _cmsPipelineSetOptimizationParameters(Dest, Lerp16Adapter, DataCLUT->Params, NULL, NULL);
     }
     else {
 
diff --git a/third_party/libopenjpeg/0046-func-ptr-mixup.patch b/third_party/libopenjpeg/0046-func-ptr-mixup.patch
new file mode 100644
index 0000000..f60e550
--- /dev/null
+++ b/third_party/libopenjpeg/0046-func-ptr-mixup.patch
@@ -0,0 +1,1449 @@
+diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
+index 9b06e7ec8..e2e048760 100644
+--- a/third_party/libopenjpeg/j2k.c
++++ b/third_party/libopenjpeg/j2k.c
+@@ -6685,8 +6685,9 @@ static OPJ_BOOL opj_j2k_read_cpf(opj_j2k_t *p_j2k,
+ /* J2K / JPT decoder interface                                             */
+ /* ----------------------------------------------------------------------- */
+ 
+-void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters)
++void opj_j2k_setup_decoder(void *p_j2k, opj_dparameters_t *parameters)
+ {
++    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
+     if (j2k && parameters) {
+         j2k->m_cp.m_specific_param.m_dec.m_layer = parameters->cp_layer;
+         j2k->m_cp.m_specific_param.m_dec.m_reduce = parameters->cp_reduce;
+@@ -6700,15 +6701,17 @@ void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters)
+     }
+ }
+ 
+-void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict)
++void opj_j2k_decoder_set_strict_mode(void *p_j2k, OPJ_BOOL strict)
+ {
++    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
+     if (j2k) {
+         j2k->m_cp.strict = strict;
+     }
+ }
+ 
+-OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
++OPJ_BOOL opj_j2k_set_threads(void *p_j2k, OPJ_UINT32 num_threads)
+ {
++    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
+     /* Currently we pass the thread-pool to the tcd, so we cannot re-set it */
+     /* afterwards */
+     if (opj_has_thread_support() && j2k->m_tcd == NULL) {
+@@ -7613,11 +7616,12 @@ static OPJ_BOOL opj_j2k_is_imf_compliant(opj_cparameters_t *parameters,
+ }
+ 
+ 
+-OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_setup_encoder(void *p_j2k,
+                                opj_cparameters_t *parameters,
+                                opj_image_t *image,
+                                opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
+     OPJ_UINT32 i, j, tileno, numpocs_tile;
+     opj_cp_t *cp = 00;
+     OPJ_UINT32 cblkw, cblkh;
+@@ -7666,10 +7670,10 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
+         return OPJ_FALSE;
+     }
+ 
+-    p_j2k->m_specific_param.m_encoder.m_nb_comps = image->numcomps;
++    j2k->m_specific_param.m_encoder.m_nb_comps = image->numcomps;
+ 
+     /* keep a link to cp so that we can destroy it later in j2k_destroy_compress */
+-    cp = &(p_j2k->m_cp);
++    cp = &(j2k->m_cp);
+ 
+     /* set default values for cp */
+     cp->tw = 1;
+@@ -7834,7 +7838,7 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
+     }
+ 
+     if (OPJ_IS_CINEMA(parameters->rsiz) || OPJ_IS_IMF(parameters->rsiz)) {
+-        p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
++        j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
+     }
+ 
+     /* Manage profiles and applications and set RSIZ */
+@@ -8379,7 +8383,7 @@ static OPJ_BOOL opj_j2k_add_tlmarker(OPJ_UINT32 tileno,
+  * -----------------------------------------------------------------------
+  */
+ 
+-OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_end_decompress(void *p_j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager
+                                )
+@@ -8391,10 +8395,11 @@ OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *p_j2k,
+ }
+ 
+ OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
+-                             opj_j2k_t* p_j2k,
++                             void* j2k,
+                              opj_image_t** p_image,
+                              opj_event_mgr_t* p_manager)
+ {
++    opj_j2k_t *p_j2k = (opj_j2k_t*)j2k;
+     /* preconditions */
+     assert(p_j2k != 00);
+     assert(p_stream != 00);
+@@ -9178,8 +9183,9 @@ static const opj_dec_memory_marker_handler_t * opj_j2k_get_marker_handler(
+     return e;
+ }
+ 
+-void opj_j2k_destroy(opj_j2k_t *p_j2k)
++void opj_j2k_destroy(void *j2k)
+ {
++    opj_j2k_t *p_j2k = (opj_j2k_t*)j2k;
+     if (p_j2k == 00) {
+         return;
+     }
+@@ -9518,7 +9524,7 @@ static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_read_tile_header(void * j2k,
+                                   OPJ_UINT32 * p_tile_index,
+                                   OPJ_UINT32 * p_data_size,
+                                   OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
+@@ -9528,6 +9534,7 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
+                                   opj_stream_private_t *p_stream,
+                                   opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 l_current_marker = J2K_MS_SOT;
+     OPJ_UINT32 l_marker_size;
+     const opj_dec_memory_marker_handler_t * l_marker_handler = 00;
+@@ -9827,13 +9834,14 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_decode_tile(void * j2k,
+                              OPJ_UINT32 p_tile_index,
+                              OPJ_BYTE * p_data,
+                              OPJ_UINT32 p_data_size,
+                              opj_stream_private_t *p_stream,
+                              opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 l_current_marker;
+     OPJ_BYTE l_data [2];
+     opj_tcp_t * l_tcp;
+@@ -10200,11 +10208,12 @@ static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_set_decoded_components(void *j2k,
+                                         OPJ_UINT32 numcomps,
+                                         const OPJ_UINT32* comps_indices,
+                                         opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 i;
+     OPJ_BOOL* already_mapped;
+ 
+@@ -10260,12 +10269,13 @@ OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+ }
+ 
+ 
+-OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_set_decode_area(void *j2k,
+                                  opj_image_t* p_image,
+                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
+                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
+                                  opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     opj_cp_t * l_cp = &(p_j2k->m_cp);
+     opj_image_t * l_image = p_j2k->m_private_image;
+     OPJ_BOOL ret;
+@@ -11200,8 +11210,9 @@ static void opj_j2k_dump_tile_info(opj_tcp_t * l_default_tile,
+     }
+ }
+ 
+-void j2k_dump(opj_j2k_t* p_j2k, OPJ_INT32 flag, FILE* out_stream)
++void j2k_dump(void* j2k, OPJ_INT32 flag, FILE* out_stream)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     /* Check if the flag is compatible with j2k file*/
+     if ((flag & OPJ_JP2_INFO) || (flag & OPJ_JP2_IND)) {
+         fprintf(out_stream, "Wrong flag\n");
+@@ -11391,8 +11402,9 @@ void j2k_dump_image_comp_header(opj_image_comp_t* comp_header,
+     }
+ }
+ 
+-opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k)
++opj_codestream_info_v2_t* j2k_get_cstr_info(void* j2k)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 compno;
+     OPJ_UINT32 numcomps = p_j2k->m_private_image->numcomps;
+     opj_tcp_t *l_default_tile;
+@@ -11467,8 +11479,9 @@ opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k)
+     return cstr_info;
+ }
+ 
+-opj_codestream_index_t* j2k_get_cstr_index(opj_j2k_t* p_j2k)
++opj_codestream_index_t* j2k_get_cstr_index(void* j2k)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     opj_codestream_index_t* l_cstr_index = (opj_codestream_index_t*)
+                                            opj_calloc(1, sizeof(opj_codestream_index_t));
+     if (!l_cstr_index) {
+@@ -11972,11 +11985,12 @@ static OPJ_BOOL opj_j2k_move_data_from_codec_to_output_image(opj_j2k_t * p_j2k,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_decode(void * j2k,
+                         opj_stream_private_t * p_stream,
+                         opj_image_t * p_image,
+                         opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     if (!p_image) {
+         return OPJ_FALSE;
+     }
+@@ -12030,12 +12044,13 @@ OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
+     return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
+ }
+ 
+-OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_get_tile(void *j2k,
+                           opj_stream_private_t *p_stream,
+                           opj_image_t* p_image,
+                           opj_event_mgr_t * p_manager,
+                           OPJ_UINT32 tile_index)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 compno;
+     OPJ_UINT32 l_tile_x, l_tile_y;
+     opj_image_comp_t* l_img_comp;
+@@ -12143,10 +12158,11 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
+     return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
+ }
+ 
+-OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_set_decoded_resolution_factor(void *j2k,
+         OPJ_UINT32 res_factor,
+         opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 it_comp;
+ 
+     p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor;
+@@ -12177,10 +12193,11 @@ OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
+ /* ----------------------------------------------------------------------- */
+ 
+ OPJ_BOOL opj_j2k_encoder_set_extra_options(
+-    opj_j2k_t *p_j2k,
++    void *j2k,
+     const char* const* p_options,
+     opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     const char* const* p_option_iter;
+ 
+     if (p_options == NULL) {
+@@ -12239,10 +12256,11 @@ OPJ_BOOL opj_j2k_encoder_set_extra_options(
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+-OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_encode(void * j2k,
+                         opj_stream_private_t *p_stream,
+                         opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     OPJ_UINT32 i, j;
+     OPJ_UINT32 l_nb_tiles;
+     OPJ_SIZE_T l_max_tile_size = 0, l_current_tile_size;
+@@ -12347,10 +12365,11 @@ OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_end_compress(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_end_compress(void *j2k,
+                               opj_stream_private_t *p_stream,
+                               opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     /* customization of the encoding */
+     if (! opj_j2k_setup_end_compress(p_j2k, p_manager)) {
+         return OPJ_FALSE;
+@@ -12363,11 +12382,12 @@ OPJ_BOOL opj_j2k_end_compress(opj_j2k_t *p_j2k,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_start_compress(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_start_compress(void *j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_image_t * p_image,
+                                 opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     /* preconditions */
+     assert(p_j2k != 00);
+     assert(p_stream != 00);
+@@ -13154,13 +13174,14 @@ static OPJ_BOOL opj_j2k_create_tcd(opj_j2k_t *p_j2k,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_j2k_write_tile(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_write_tile(void * j2k,
+                             OPJ_UINT32 p_tile_index,
+                             OPJ_BYTE * p_data,
+                             OPJ_UINT32 p_data_size,
+                             opj_stream_private_t *p_stream,
+                             opj_event_mgr_t * p_manager)
+ {
++    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
+     if (! opj_j2k_pre_write_tile(p_j2k, p_tile_index, p_stream, p_manager)) {
+         opj_event_msg(p_manager, EVT_ERROR,
+                       "Error while opj_j2k_pre_write_tile with tile index = %d\n", p_tile_index);
+diff --git a/third_party/libopenjpeg/j2k.h b/third_party/libopenjpeg/j2k.h
+index 04fba645a..1d824c019 100644
+--- a/third_party/libopenjpeg/j2k.h
++++ b/third_party/libopenjpeg/j2k.h
+@@ -621,15 +621,15 @@ opj_j2k_t;
+ 
+ /**
+ Setup the decoder decoding parameters using user parameters.
+-Decoding parameters are returned in j2k->cp.
+-@param j2k J2K decompressor handle
++Decoding parameters are returned in p_j2k->cp.
++@param p_j2k J2K decompressor handle
+ @param parameters decompression parameters
+ */
+-void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters);
++void opj_j2k_setup_decoder(void *p_j2k, opj_dparameters_t *parameters);
+ 
+-void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict);
++void opj_j2k_decoder_set_strict_mode(void *j2k, OPJ_BOOL strict);
+ 
+-OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads);
++OPJ_BOOL opj_j2k_set_threads(void *j2k, OPJ_UINT32 num_threads);
+ 
+ /**
+  * Creates a J2K compression structure
+@@ -639,7 +639,7 @@ OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads);
+ opj_j2k_t* opj_j2k_create_compress(void);
+ 
+ 
+-OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_setup_encoder(void *p_j2k,
+                                opj_cparameters_t *parameters,
+                                opj_image_t *image,
+                                opj_event_mgr_t * p_manager);
+@@ -658,7 +658,7 @@ const char *opj_j2k_convert_progression_order(OPJ_PROG_ORDER prg_order);
+  * Ends the decompression procedures and possibiliy add data to be read after the
+  * codestream.
+  */
+-OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *j2k,
++OPJ_BOOL opj_j2k_end_decompress(void *j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_event_mgr_t * p_manager);
+ 
+@@ -666,14 +666,14 @@ OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *j2k,
+  * Reads a jpeg2000 codestream header structure.
+  *
+  * @param p_stream the stream to read data from.
+- * @param p_j2k the jpeg2000 codec.
++ * @param j2k the jpeg2000 codec.
+  * @param p_image FIXME DOC
+  * @param p_manager the user event manager.
+  *
+  * @return true if the box is valid.
+  */
+ OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
+-                             opj_j2k_t* p_j2k,
++                             void* j2k,
+                              opj_image_t** p_image,
+                              opj_event_mgr_t* p_manager);
+ 
+@@ -681,9 +681,9 @@ OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
+ /**
+  * Destroys a jpeg2000 codec.
+  *
+- * @param   p_j2k   the jpeg20000 structure to destroy.
++ * @param   j2k   the jpeg20000 structure to destroy.
+  */
+-void opj_j2k_destroy(opj_j2k_t *p_j2k);
++void opj_j2k_destroy(void *j2k);
+ 
+ /**
+  * Destroys a codestream index structure.
+@@ -694,14 +694,14 @@ void j2k_destroy_cstr_index(opj_codestream_index_t *p_cstr_ind);
+ 
+ /**
+  * Decode tile data.
+- * @param   p_j2k       the jpeg2000 codec.
++ * @param   j2k       the jpeg2000 codec.
+  * @param   p_tile_index
+  * @param p_data       FIXME DOC
+  * @param p_data_size  FIXME DOC
+  * @param   p_stream            the stream to write data to.
+  * @param   p_manager   the user event manager.
+  */
+-OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_decode_tile(void * j2k,
+                              OPJ_UINT32 p_tile_index,
+                              OPJ_BYTE * p_data,
+                              OPJ_UINT32 p_data_size,
+@@ -710,7 +710,7 @@ OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
+ 
+ /**
+  * Reads a tile header.
+- * @param   p_j2k       the jpeg2000 codec.
++ * @param   j2k       the jpeg2000 codec.
+  * @param   p_tile_index FIXME DOC
+  * @param   p_data_size FIXME DOC
+  * @param   p_tile_x0 FIXME DOC
+@@ -722,7 +722,7 @@ OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
+  * @param   p_stream            the stream to write data to.
+  * @param   p_manager   the user event manager.
+  */
+-OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_read_tile_header(void * j2k,
+                                   OPJ_UINT32 * p_tile_index,
+                                   OPJ_UINT32 * p_data_size,
+                                   OPJ_INT32 * p_tile_x0,
+@@ -737,7 +737,7 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
+ 
+ /** Sets the indices of the components to decode.
+  *
+- * @param p_j2k         the jpeg2000 codec.
++ * @param j2k         the jpeg2000 codec.
+  * @param numcomps      Number of components to decode.
+  * @param comps_indices Array of num_compts indices (numbering starting at 0)
+  *                      corresponding to the components to decode.
+@@ -745,7 +745,7 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
+  *
+  * @return OPJ_TRUE in case of success.
+  */
+-OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_set_decoded_components(void *j2k,
+                                         OPJ_UINT32 numcomps,
+                                         const OPJ_UINT32* comps_indices,
+                                         opj_event_mgr_t * p_manager);
+@@ -753,7 +753,7 @@ OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+ /**
+  * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
+  *
+- * @param   p_j2k           the jpeg2000 codec.
++ * @param   j2k           the jpeg2000 codec.
+  * @param   p_image     FIXME DOC
+  * @param   p_start_x       the left position of the rectangle to decode (in image coordinates).
+  * @param   p_start_y       the up position of the rectangle to decode (in image coordinates).
+@@ -763,7 +763,7 @@ OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+  *
+  * @return  true            if the area could be set.
+  */
+-OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_set_decode_area(void *j2k,
+                                  opj_image_t* p_image,
+                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
+                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
+@@ -780,12 +780,12 @@ opj_j2k_t* opj_j2k_create_decompress(void);
+ /**
+  * Dump some elements from the J2K decompression structure .
+  *
+- *@param p_j2k              the jpeg2000 codec.
++ *@param j2k                the jpeg2000 codec.
+  *@param flag               flag to describe what elements are dump.
+  *@param out_stream         output stream where dump the elements.
+  *
+ */
+-void j2k_dump(opj_j2k_t* p_j2k, OPJ_INT32 flag, FILE* out_stream);
++void j2k_dump(void* j2k, OPJ_INT32 flag, FILE* out_stream);
+ 
+ 
+ 
+@@ -812,20 +812,20 @@ void j2k_dump_image_comp_header(opj_image_comp_t* comp, OPJ_BOOL dev_dump_flag,
+ /**
+  * Get the codestream info from a JPEG2000 codec.
+  *
+- *@param    p_j2k               the component image header to dump.
++ *@param    j2k               the component image header to dump.
+  *
+  *@return   the codestream information extract from the jpg2000 codec
+  */
+-opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k);
++opj_codestream_info_v2_t* j2k_get_cstr_info(void* j2k);
+ 
+ /**
+  * Get the codestream index from a JPEG2000 codec.
+  *
+- *@param    p_j2k               the component image header to dump.
++ *@param    j2k               the component image header to dump.
+  *
+  *@return   the codestream index extract from the jpg2000 codec
+  */
+-opj_codestream_index_t* j2k_get_cstr_index(opj_j2k_t* p_j2k);
++opj_codestream_index_t* j2k_get_cstr_index(void* j2k);
+ 
+ /**
+  * Decode an image from a JPEG-2000 codestream
+@@ -835,46 +835,46 @@ opj_codestream_index_t* j2k_get_cstr_index(opj_j2k_t* p_j2k);
+  * @param p_manager FIXME DOC
+  * @return FIXME DOC
+ */
+-OPJ_BOOL opj_j2k_decode(opj_j2k_t *j2k,
++OPJ_BOOL opj_j2k_decode(void *j2k,
+                         opj_stream_private_t *p_stream,
+                         opj_image_t *p_image,
+                         opj_event_mgr_t *p_manager);
+ 
+ 
+-OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_get_tile(void *j2k,
+                           opj_stream_private_t *p_stream,
+                           opj_image_t* p_image,
+                           opj_event_mgr_t * p_manager,
+                           OPJ_UINT32 tile_index);
+ 
+-OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_set_decoded_resolution_factor(void *j2k,
+         OPJ_UINT32 res_factor,
+         opj_event_mgr_t * p_manager);
+ 
+ /**
+  * Specify extra options for the encoder.
+  *
+- * @param  p_j2k        the jpeg2000 codec.
++ * @param  j2k          the jpeg2000 codec.
+  * @param  p_options    options
+  * @param  p_manager    the user event manager
+  *
+  * @see opj_encoder_set_extra_options() for more details.
+  */
+ OPJ_BOOL opj_j2k_encoder_set_extra_options(
+-    opj_j2k_t *p_j2k,
++    void *j2k,
+     const char* const* p_options,
+     opj_event_mgr_t * p_manager);
+ 
+ /**
+  * Writes a tile.
+- * @param   p_j2k       the jpeg2000 codec.
++ * @param   j2k         the jpeg2000 codec.
+  * @param p_tile_index FIXME DOC
+  * @param p_data FIXME DOC
+  * @param p_data_size FIXME DOC
+  * @param   p_stream            the stream to write data to.
+  * @param   p_manager   the user event manager.
+  */
+-OPJ_BOOL opj_j2k_write_tile(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_write_tile(void * j2k,
+                             OPJ_UINT32 p_tile_index,
+                             OPJ_BYTE * p_data,
+                             OPJ_UINT32 p_data_size,
+@@ -884,21 +884,21 @@ OPJ_BOOL opj_j2k_write_tile(opj_j2k_t * p_j2k,
+ /**
+  * Encodes an image into a JPEG-2000 codestream
+  */
+-OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
++OPJ_BOOL opj_j2k_encode(void * j2k,
+                         opj_stream_private_t *cio,
+                         opj_event_mgr_t * p_manager);
+ 
+ /**
+  * Starts a compression scheme, i.e. validates the codec parameters, writes the header.
+  *
+- * @param   p_j2k       the jpeg2000 codec.
++ * @param   j2k         the jpeg2000 codec.
+  * @param   p_stream            the stream object.
+  * @param   p_image FIXME DOC
+  * @param   p_manager   the user event manager.
+  *
+  * @return true if the codec is valid.
+  */
+-OPJ_BOOL opj_j2k_start_compress(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_start_compress(void *j2k,
+                                 opj_stream_private_t *p_stream,
+                                 opj_image_t * p_image,
+                                 opj_event_mgr_t * p_manager);
+@@ -907,7 +907,7 @@ OPJ_BOOL opj_j2k_start_compress(opj_j2k_t *p_j2k,
+  * Ends the compression procedures and possibiliy add data to be read after the
+  * codestream.
+  */
+-OPJ_BOOL opj_j2k_end_compress(opj_j2k_t *p_j2k,
++OPJ_BOOL opj_j2k_end_compress(void *j2k,
+                               opj_stream_private_t *cio,
+                               opj_event_mgr_t * p_manager);
+ 
+diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
+index 44d0c98e5..6db728d18 100644
+--- a/third_party/libopenjpeg/jp2.c
++++ b/third_party/libopenjpeg/jp2.c
+@@ -1609,11 +1609,12 @@ static OPJ_BOOL opj_jp2_read_colr(opj_jp2_t *jp2,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_decode(void *p_jp2,
+                         opj_stream_private_t *p_stream,
+                         opj_image_t* p_image,
+                         opj_event_mgr_t * p_manager)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     if (!p_image) {
+         return OPJ_FALSE;
+     }
+@@ -1905,8 +1906,9 @@ static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2,
+ /* JP2 decoder interface                                             */
+ /* ----------------------------------------------------------------------- */
+ 
+-void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
++void opj_jp2_setup_decoder(void *p_jp2, opj_dparameters_t *parameters)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     /* setup the J2K codec */
+     opj_j2k_setup_decoder(jp2->j2k, parameters);
+ 
+@@ -1917,13 +1919,15 @@ void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
+                                  OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
+ }
+ 
+-void opj_jp2_decoder_set_strict_mode(opj_jp2_t *jp2, OPJ_BOOL strict)
++void opj_jp2_decoder_set_strict_mode(void *p_jp2, OPJ_BOOL strict)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     opj_j2k_decoder_set_strict_mode(jp2->j2k, strict);
+ }
+ 
+-OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads)
++OPJ_BOOL opj_jp2_set_threads(void *p_jp2, OPJ_UINT32 num_threads)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     return opj_j2k_set_threads(jp2->j2k, num_threads);
+ }
+ 
+@@ -1931,11 +1935,12 @@ OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads)
+ /* JP2 encoder interface                                             */
+ /* ----------------------------------------------------------------------- */
+ 
+-OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_setup_encoder(void *p_jp2,
+                                opj_cparameters_t *parameters,
+                                opj_image_t *image,
+                                opj_event_mgr_t * p_manager)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     OPJ_UINT32 i;
+     OPJ_UINT32 depth_0;
+     OPJ_UINT32 sign;
+@@ -2118,18 +2123,20 @@ OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_jp2_encode(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_encode(void *p_jp2,
+                         opj_stream_private_t *stream,
+                         opj_event_mgr_t * p_manager)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     return opj_j2k_encode(jp2->j2k, stream, p_manager);
+ }
+ 
+-OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_end_decompress(void *p_jp2,
+                                 opj_stream_private_t *cio,
+                                 opj_event_mgr_t * p_manager
+                                )
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     /* preconditions */
+     assert(jp2 != 00);
+     assert(cio != 00);
+@@ -2148,11 +2155,12 @@ OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
+     return opj_j2k_end_decompress(jp2->j2k, cio, p_manager);
+ }
+ 
+-OPJ_BOOL opj_jp2_end_compress(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_end_compress(void *p_jp2,
+                               opj_stream_private_t *cio,
+                               opj_event_mgr_t * p_manager
+                              )
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     /* preconditions */
+     assert(jp2 != 00);
+     assert(cio != 00);
+@@ -2476,12 +2484,13 @@ static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2,
+     return l_result;
+ }
+ 
+-OPJ_BOOL opj_jp2_start_compress(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_start_compress(void *p_jp2,
+                                 opj_stream_private_t *stream,
+                                 opj_image_t * p_image,
+                                 opj_event_mgr_t * p_manager
+                                )
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     /* preconditions */
+     assert(jp2 != 00);
+     assert(stream != 00);
+@@ -2854,11 +2863,12 @@ static OPJ_BOOL opj_jp2_read_boxhdr_char(opj_jp2_box_t *box,
+ }
+ 
+ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
+-                             opj_jp2_t *jp2,
++                             void *p_jp2,
+                              opj_image_t ** p_image,
+                              opj_event_mgr_t * p_manager
+                             )
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     /* preconditions */
+     assert(jp2 != 00);
+     assert(p_stream != 00);
+@@ -2981,7 +2991,7 @@ static OPJ_BOOL opj_jp2_setup_header_reading(opj_jp2_t *jp2,
+     return OPJ_TRUE;
+ }
+ 
+-OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
++OPJ_BOOL opj_jp2_read_tile_header(void *p_jp2,
+                                   OPJ_UINT32 * p_tile_index,
+                                   OPJ_UINT32 * p_data_size,
+                                   OPJ_INT32 * p_tile_x0,
+@@ -2994,7 +3004,8 @@ OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
+                                   opj_event_mgr_t * p_manager
+                                  )
+ {
+-    return opj_j2k_read_tile_header(p_jp2->j2k,
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_read_tile_header(jp2->j2k,
+                                     p_tile_index,
+                                     p_data_size,
+                                     p_tile_x0, p_tile_y0,
+@@ -3005,7 +3016,7 @@ OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
+                                     p_manager);
+ }
+ 
+-OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_write_tile(void *p_jp2,
+                             OPJ_UINT32 p_tile_index,
+                             OPJ_BYTE * p_data,
+                             OPJ_UINT32 p_data_size,
+@@ -3014,11 +3025,12 @@ OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
+                            )
+ 
+ {
+-    return opj_j2k_write_tile(p_jp2->j2k, p_tile_index, p_data, p_data_size,
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_write_tile(jp2->j2k, p_tile_index, p_data, p_data_size,
+                               p_stream, p_manager);
+ }
+ 
+-OPJ_BOOL opj_jp2_decode_tile(opj_jp2_t * p_jp2,
++OPJ_BOOL opj_jp2_decode_tile(void *p_jp2,
+                              OPJ_UINT32 p_tile_index,
+                              OPJ_BYTE * p_data,
+                              OPJ_UINT32 p_data_size,
+@@ -3026,12 +3038,14 @@ OPJ_BOOL opj_jp2_decode_tile(opj_jp2_t * p_jp2,
+                              opj_event_mgr_t * p_manager
+                             )
+ {
+-    return opj_j2k_decode_tile(p_jp2->j2k, p_tile_index, p_data, p_data_size,
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_decode_tile(jp2->j2k, p_tile_index, p_data, p_data_size,
+                                p_stream, p_manager);
+ }
+ 
+-void opj_jp2_destroy(opj_jp2_t *jp2)
++void opj_jp2_destroy(void *p_jp2)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     if (jp2) {
+         /* destroy the J2K codec */
+         opj_j2k_destroy(jp2->j2k);
+@@ -3098,34 +3112,37 @@ void opj_jp2_destroy(opj_jp2_t *jp2)
+     }
+ }
+ 
+-OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_set_decoded_components(void *p_jp2,
+                                         OPJ_UINT32 numcomps,
+                                         const OPJ_UINT32* comps_indices,
+                                         opj_event_mgr_t * p_manager)
+ {
+-    return opj_j2k_set_decoded_components(p_jp2->j2k,
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_set_decoded_components(jp2->j2k,
+                                           numcomps, comps_indices,
+                                           p_manager);
+ }
+ 
+-OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_set_decode_area(void *p_jp2,
+                                  opj_image_t* p_image,
+                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
+                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
+                                  opj_event_mgr_t * p_manager
+                                 )
+ {
+-    return opj_j2k_set_decode_area(p_jp2->j2k, p_image, p_start_x, p_start_y,
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_set_decode_area(jp2->j2k, p_image, p_start_x, p_start_y,
+                                    p_end_x, p_end_y, p_manager);
+ }
+ 
+-OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_get_tile(void *jp2,
+                           opj_stream_private_t *p_stream,
+                           opj_image_t* p_image,
+                           opj_event_mgr_t * p_manager,
+                           OPJ_UINT32 tile_index
+                          )
+ {
++    opj_jp2_t *p_jp2 = (opj_jp2_t*)jp2;
+     if (!p_image) {
+         return OPJ_FALSE;
+     }
+@@ -3234,41 +3251,46 @@ opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder)
+     return jp2;
+ }
+ 
+-void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream)
++void jp2_dump(void* p_jp2, OPJ_INT32 flag, FILE* out_stream)
+ {
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+     /* preconditions */
+     assert(p_jp2 != 00);
+ 
+-    j2k_dump(p_jp2->j2k,
++    j2k_dump(jp2->j2k,
+              flag,
+              out_stream);
+ }
+ 
+-opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2)
++opj_codestream_index_t* jp2_get_cstr_index(void* p_jp2)
+ {
+-    return j2k_get_cstr_index(p_jp2->j2k);
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return j2k_get_cstr_index(jp2->j2k);
+ }
+ 
+-opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2)
++opj_codestream_info_v2_t* jp2_get_cstr_info(void* p_jp2)
+ {
+-    return j2k_get_cstr_info(p_jp2->j2k);
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return j2k_get_cstr_info(jp2->j2k);
+ }
+ 
+-OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_set_decoded_resolution_factor(void *p_jp2,
+         OPJ_UINT32 res_factor,
+         opj_event_mgr_t * p_manager)
+ {
+-    return opj_j2k_set_decoded_resolution_factor(p_jp2->j2k, res_factor, p_manager);
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_set_decoded_resolution_factor(jp2->j2k, res_factor, p_manager);
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+ OPJ_BOOL opj_jp2_encoder_set_extra_options(
+-    opj_jp2_t *p_jp2,
++    void *p_jp2,
+     const char* const* p_options,
+     opj_event_mgr_t * p_manager)
+ {
+-    return opj_j2k_encoder_set_extra_options(p_jp2->j2k, p_options, p_manager);
++    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
++    return opj_j2k_encoder_set_extra_options(jp2->j2k, p_options, p_manager);
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+diff --git a/third_party/libopenjpeg/jp2.h b/third_party/libopenjpeg/jp2.h
+index 173f25119..fd9175a4e 100644
+--- a/third_party/libopenjpeg/jp2.h
++++ b/third_party/libopenjpeg/jp2.h
+@@ -230,38 +230,38 @@ opj_jp2_img_header_writer_handler_t;
+ /**
+ Setup the decoder decoding parameters using user parameters.
+ Decoding parameters are returned in jp2->j2k->cp.
+-@param jp2 JP2 decompressor handle
++@param p_jp2 JP2 decompressor handle
+ @param parameters decompression parameters
+ */
+-void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters);
++void opj_jp2_setup_decoder(void *p_jp2, opj_dparameters_t *parameters);
+ 
+ /**
+ Set the strict mode parameter.  When strict mode is enabled, the entire
+ bitstream must be decoded or an error is returned.  When it is disabled,
+ the decoder will decode partial bitstreams.
+-@param jp2 JP2 decompressor handle
++@param p_jp2 JP2 decompressor handle
+ @param strict OPJ_TRUE for strict mode
+ */
+-void opj_jp2_decoder_set_strict_mode(opj_jp2_t *jp2, OPJ_BOOL strict);
++void opj_jp2_decoder_set_strict_mode(void *p_jp2, OPJ_BOOL strict);
+ 
+ /** Allocates worker threads for the compressor/decompressor.
+  *
+- * @param jp2 JP2 decompressor handle
++ * @param p_jp2 JP2 decompressor handle
+  * @param num_threads Number of threads.
+  * @return OPJ_TRUE in case of success.
+  */
+-OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads);
++OPJ_BOOL opj_jp2_set_threads(void *p_jp2, OPJ_UINT32 num_threads);
+ 
+ /**
+  * Decode an image from a JPEG-2000 file stream
+- * @param jp2 JP2 decompressor handle
++ * @param p_jp2 JP2 decompressor handle
+  * @param p_stream  FIXME DOC
+  * @param p_image   FIXME DOC
+  * @param p_manager FIXME DOC
+  *
+  * @return Returns a decoded image if successful, returns NULL otherwise
+ */
+-OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_decode(void *p_jp2,
+                         opj_stream_private_t *p_stream,
+                         opj_image_t* p_image,
+                         opj_event_mgr_t * p_manager);
+@@ -270,25 +270,25 @@ OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
+  * Setup the encoder parameters using the current image and using user parameters.
+  * Coding parameters are returned in jp2->j2k->cp.
+  *
+- * @param jp2 JP2 compressor handle
++ * @param p_jp2 JP2 compressor handle
+  * @param parameters compression parameters
+  * @param image input filled image
+  * @param p_manager  FIXME DOC
+  * @return OPJ_TRUE if successful, OPJ_FALSE otherwise
+ */
+-OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_setup_encoder(void *p_jp2,
+                                opj_cparameters_t *parameters,
+                                opj_image_t *image,
+                                opj_event_mgr_t * p_manager);
+ 
+ /**
+ Encode an image into a JPEG-2000 file stream
+-@param jp2      JP2 compressor handle
++@param p_jp2      JP2 compressor handle
+ @param stream    Output buffer stream
+ @param p_manager  event manager
+ @return Returns true if successful, returns false otherwise
+ */
+-OPJ_BOOL opj_jp2_encode(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_encode(void *p_jp2,
+                         opj_stream_private_t *stream,
+                         opj_event_mgr_t * p_manager);
+ 
+@@ -296,14 +296,14 @@ OPJ_BOOL opj_jp2_encode(opj_jp2_t *jp2,
+ /**
+  * Starts a compression scheme, i.e. validates the codec parameters, writes the header.
+  *
+- * @param  jp2    the jpeg2000 file codec.
++ * @param  p_jp2    the jpeg2000 file codec.
+  * @param  stream    the stream object.
+  * @param  p_image   FIXME DOC
+  * @param p_manager FIXME DOC
+  *
+  * @return true if the codec is valid.
+  */
+-OPJ_BOOL opj_jp2_start_compress(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_start_compress(void *p_jp2,
+                                 opj_stream_private_t *stream,
+                                 opj_image_t * p_image,
+                                 opj_event_mgr_t * p_manager);
+@@ -313,7 +313,7 @@ OPJ_BOOL opj_jp2_start_compress(opj_jp2_t *jp2,
+  * Ends the compression procedures and possibiliy add data to be read after the
+  * codestream.
+  */
+-OPJ_BOOL opj_jp2_end_compress(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_end_compress(void *p_jp2,
+                               opj_stream_private_t *cio,
+                               opj_event_mgr_t * p_manager);
+ 
+@@ -323,7 +323,7 @@ OPJ_BOOL opj_jp2_end_compress(opj_jp2_t *jp2,
+  * Ends the decompression procedures and possibiliy add data to be read after the
+  * codestream.
+  */
+-OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_end_decompress(void *p_jp2,
+                                 opj_stream_private_t *cio,
+                                 opj_event_mgr_t * p_manager);
+ 
+@@ -331,20 +331,20 @@ OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
+  * Reads a jpeg2000 file header structure.
+  *
+  * @param p_stream the stream to read data from.
+- * @param jp2 the jpeg2000 file header structure.
++ * @param p_jp2 the jpeg2000 file header structure.
+  * @param p_image   FIXME DOC
+  * @param p_manager the user event manager.
+  *
+  * @return true if the box is valid.
+  */
+ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
+-                             opj_jp2_t *jp2,
++                             void *p_jp2,
+                              opj_image_t ** p_image,
+                              opj_event_mgr_t * p_manager);
+ 
+ /** Sets the indices of the components to decode.
+  *
+- * @param jp2 JP2 decompressor handle
++ * @param p_jp2 JP2 decompressor handle
+  * @param numcomps Number of components to decode.
+  * @param comps_indices Array of num_compts indices (numbering starting at 0)
+  *                     corresponding to the components to decode.
+@@ -352,7 +352,7 @@ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
+  *
+  * @return OPJ_TRUE in case of success.
+  */
+-OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *jp2,
++OPJ_BOOL opj_jp2_set_decoded_components(void *p_jp2,
+                                         OPJ_UINT32 numcomps,
+                                         const OPJ_UINT32* comps_indices,
+                                         opj_event_mgr_t * p_manager);
+@@ -371,7 +371,7 @@ OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *jp2,
+  * @param  p_stream      the stream to write data to.
+  * @param  p_manager     the user event manager.
+  */
+-OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
++OPJ_BOOL opj_jp2_read_tile_header(void * p_jp2,
+                                   OPJ_UINT32 * p_tile_index,
+                                   OPJ_UINT32 * p_data_size,
+                                   OPJ_INT32 * p_tile_x0,
+@@ -393,7 +393,7 @@ OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
+  * @param  p_stream      the stream to write data to.
+  * @param  p_manager  the user event manager.
+  */
+-OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_write_tile(void *p_jp2,
+                             OPJ_UINT32 p_tile_index,
+                             OPJ_BYTE * p_data,
+                             OPJ_UINT32 p_data_size,
+@@ -411,7 +411,7 @@ OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
+  *
+  * @return FIXME DOC
+  */
+-OPJ_BOOL opj_jp2_decode_tile(opj_jp2_t * p_jp2,
++OPJ_BOOL opj_jp2_decode_tile(void * p_jp2,
+                              OPJ_UINT32 p_tile_index,
+                              OPJ_BYTE * p_data,
+                              OPJ_UINT32 p_data_size,
+@@ -427,9 +427,9 @@ opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder);
+ 
+ /**
+ Destroy a JP2 decompressor handle
+-@param jp2 JP2 decompressor handle to destroy
++@param p_jp2 JP2 decompressor handle to destroy
+ */
+-void opj_jp2_destroy(opj_jp2_t *jp2);
++void opj_jp2_destroy(void *p_jp2);
+ 
+ 
+ /**
+@@ -445,7 +445,7 @@ void opj_jp2_destroy(opj_jp2_t *jp2);
+  *
+  * @return  true      if the area could be set.
+  */
+-OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_set_decode_area(void *p_jp2,
+                                  opj_image_t* p_image,
+                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
+                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
+@@ -454,7 +454,7 @@ OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *p_jp2,
+ /**
+ *
+ */
+-OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_get_tile(void *jp2,
+                           opj_stream_private_t *p_stream,
+                           opj_image_t* p_image,
+                           opj_event_mgr_t * p_manager,
+@@ -464,7 +464,7 @@ OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
+ /**
+  *
+  */
+-OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
++OPJ_BOOL opj_jp2_set_decoded_resolution_factor(void *p_jp2,
+         OPJ_UINT32 res_factor,
+         opj_event_mgr_t * p_manager);
+ 
+@@ -478,7 +478,7 @@ OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
+  * @see opj_encoder_set_extra_options() for more details.
+  */
+ OPJ_BOOL opj_jp2_encoder_set_extra_options(
+-    opj_jp2_t *p_jp2,
++    void *p_jp2,
+     const char* const* p_options,
+     opj_event_mgr_t * p_manager);
+ 
+@@ -492,7 +492,7 @@ OPJ_BOOL opj_jp2_encoder_set_extra_options(
+  *@param out_stream      output stream where dump the elements.
+  *
+ */
+-void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream);
++void jp2_dump(void* p_jp2, OPJ_INT32 flag, FILE* out_stream);
+ 
+ /**
+  * Get the codestream info from a JPEG2000 codec.
+@@ -501,7 +501,7 @@ void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream);
+  *
+  *@return  the codestream information extract from the jpg2000 codec
+  */
+-opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2);
++opj_codestream_info_v2_t* jp2_get_cstr_info(void* p_jp2);
+ 
+ /**
+  * Get the codestream index from a JPEG2000 codec.
+@@ -510,7 +510,7 @@ opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2);
+  *
+  *@return  the codestream index extract from the jpg2000 codec
+  */
+-opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2);
++opj_codestream_index_t* jp2_get_cstr_index(void* p_jp2);
+ 
+ 
+ /*@}*/
+diff --git a/third_party/libopenjpeg/openjpeg.c b/third_party/libopenjpeg/openjpeg.c
+index 29d3ee528..9dd4256d7 100644
+--- a/third_party/libopenjpeg/openjpeg.c
++++ b/third_party/libopenjpeg/openjpeg.c
+@@ -189,85 +189,48 @@ opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
+ 
+     switch (p_format) {
+     case OPJ_CODEC_J2K:
+-        l_codec->opj_dump_codec = (void (*)(void*, OPJ_INT32, FILE*)) j2k_dump;
++        l_codec->opj_dump_codec = j2k_dump;
+ 
+-        l_codec->opj_get_codec_info = (opj_codestream_info_v2_t* (*)(
+-                                           void*)) j2k_get_cstr_info;
++        l_codec->opj_get_codec_info = j2k_get_cstr_info;
+ 
+-        l_codec->opj_get_codec_index = (opj_codestream_index_t* (*)(
+-                                            void*)) j2k_get_cstr_index;
++        l_codec->opj_get_codec_index = j2k_get_cstr_index;
+ 
+-        l_codec->m_codec_data.m_decompression.opj_decode =
+-            (OPJ_BOOL(*)(void *,
+-                         struct opj_stream_private *,
+-                         opj_image_t*, struct opj_event_mgr *)) opj_j2k_decode;
++        l_codec->m_codec_data.m_decompression.opj_decode = opj_j2k_decode;
+ 
+         l_codec->m_codec_data.m_decompression.opj_end_decompress =
+-            (OPJ_BOOL(*)(void *,
+-                         struct opj_stream_private *,
+-                         struct opj_event_mgr *)) opj_j2k_end_decompress;
++            opj_j2k_end_decompress;
+ 
+         l_codec->m_codec_data.m_decompression.opj_read_header =
+-            (OPJ_BOOL(*)(struct opj_stream_private *,
+-                         void *,
+-                         opj_image_t **,
+-                         struct opj_event_mgr *)) opj_j2k_read_header;
++            opj_j2k_read_header;
+ 
+-        l_codec->m_codec_data.m_decompression.opj_destroy =
+-            (void (*)(void *))opj_j2k_destroy;
++        l_codec->m_codec_data.m_decompression.opj_destroy = opj_j2k_destroy;
+ 
+         l_codec->m_codec_data.m_decompression.opj_setup_decoder =
+-            (void (*)(void *, opj_dparameters_t *)) opj_j2k_setup_decoder;
++            opj_j2k_setup_decoder;
+ 
+         l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
+-            (void (*)(void *, OPJ_BOOL)) opj_j2k_decoder_set_strict_mode;
++            opj_j2k_decoder_set_strict_mode;
+ 
+ 
+         l_codec->m_codec_data.m_decompression.opj_read_tile_header =
+-            (OPJ_BOOL(*)(void *,
+-                         OPJ_UINT32*,
+-                         OPJ_UINT32*,
+-                         OPJ_INT32*, OPJ_INT32*,
+-                         OPJ_INT32*, OPJ_INT32*,
+-                         OPJ_UINT32*,
+-                         OPJ_BOOL*,
+-                         struct opj_stream_private *,
+-                         struct opj_event_mgr *)) opj_j2k_read_tile_header;
++            opj_j2k_read_tile_header;
+ 
+         l_codec->m_codec_data.m_decompression.opj_decode_tile_data =
+-            (OPJ_BOOL(*)(void *,
+-                         OPJ_UINT32,
+-                         OPJ_BYTE*,
+-                         OPJ_UINT32,
+-                         struct opj_stream_private *,
+-                         struct opj_event_mgr *)) opj_j2k_decode_tile;
++            opj_j2k_decode_tile;
+ 
+         l_codec->m_codec_data.m_decompression.opj_set_decode_area =
+-            (OPJ_BOOL(*)(void *,
+-                         opj_image_t*,
+-                         OPJ_INT32, OPJ_INT32, OPJ_INT32, OPJ_INT32,
+-                         struct opj_event_mgr *)) opj_j2k_set_decode_area;
++            opj_j2k_set_decode_area;
+ 
+         l_codec->m_codec_data.m_decompression.opj_get_decoded_tile =
+-            (OPJ_BOOL(*)(void *p_codec,
+-                         opj_stream_private_t *p_cio,
+-                         opj_image_t *p_image,
+-                         struct opj_event_mgr * p_manager,
+-                         OPJ_UINT32 tile_index)) opj_j2k_get_tile;
++            opj_j2k_get_tile;
+ 
+         l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor =
+-            (OPJ_BOOL(*)(void * p_codec,
+-                         OPJ_UINT32 res_factor,
+-                         struct opj_event_mgr * p_manager)) opj_j2k_set_decoded_resolution_factor;
++            opj_j2k_set_decoded_resolution_factor;
+ 
+         l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
+-            (OPJ_BOOL(*)(void * p_codec,
+-                         OPJ_UINT32 numcomps,
+-                         const OPJ_UINT32 * comps_indices,
+-                         struct opj_event_mgr * p_manager)) opj_j2k_set_decoded_components;
++            opj_j2k_set_decoded_components;
+ 
+-        l_codec->opj_set_threads =
+-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_j2k_set_threads;
++        l_codec->opj_set_threads = opj_j2k_set_threads;
+ 
+         l_codec->m_codec = opj_j2k_create_decompress();
+ 
+@@ -280,85 +243,47 @@ opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
+ 
+     case OPJ_CODEC_JP2:
+         /* get a JP2 decoder handle */
+-        l_codec->opj_dump_codec = (void (*)(void*, OPJ_INT32, FILE*)) jp2_dump;
++        l_codec->opj_dump_codec = jp2_dump;
+ 
+-        l_codec->opj_get_codec_info = (opj_codestream_info_v2_t* (*)(
+-                                           void*)) jp2_get_cstr_info;
++        l_codec->opj_get_codec_info = jp2_get_cstr_info;
+ 
+-        l_codec->opj_get_codec_index = (opj_codestream_index_t* (*)(
+-                                            void*)) jp2_get_cstr_index;
++        l_codec->opj_get_codec_index = jp2_get_cstr_index;
+ 
+-        l_codec->m_codec_data.m_decompression.opj_decode =
+-            (OPJ_BOOL(*)(void *,
+-                         struct opj_stream_private *,
+-                         opj_image_t*,
+-                         struct opj_event_mgr *)) opj_jp2_decode;
++        l_codec->m_codec_data.m_decompression.opj_decode = opj_jp2_decode;
+ 
+         l_codec->m_codec_data.m_decompression.opj_end_decompress =
+-            (OPJ_BOOL(*)(void *,
+-                         struct opj_stream_private *,
+-                         struct opj_event_mgr *)) opj_jp2_end_decompress;
++            opj_jp2_end_decompress;
+ 
+         l_codec->m_codec_data.m_decompression.opj_read_header =
+-            (OPJ_BOOL(*)(struct opj_stream_private *,
+-                         void *,
+-                         opj_image_t **,
+-                         struct opj_event_mgr *)) opj_jp2_read_header;
++            opj_jp2_read_header;
+ 
+         l_codec->m_codec_data.m_decompression.opj_read_tile_header =
+-            (OPJ_BOOL(*)(void *,
+-                         OPJ_UINT32*,
+-                         OPJ_UINT32*,
+-                         OPJ_INT32*,
+-                         OPJ_INT32*,
+-                         OPJ_INT32 *,
+-                         OPJ_INT32 *,
+-                         OPJ_UINT32 *,
+-                         OPJ_BOOL *,
+-                         struct opj_stream_private *,
+-                         struct opj_event_mgr *)) opj_jp2_read_tile_header;
++            opj_jp2_read_tile_header;
+ 
+         l_codec->m_codec_data.m_decompression.opj_decode_tile_data =
+-            (OPJ_BOOL(*)(void *,
+-                         OPJ_UINT32, OPJ_BYTE*, OPJ_UINT32,
+-                         struct opj_stream_private *,
+-                         struct opj_event_mgr *)) opj_jp2_decode_tile;
++            opj_jp2_decode_tile;
+ 
+-        l_codec->m_codec_data.m_decompression.opj_destroy = (void (*)(
+-                    void *))opj_jp2_destroy;
++        l_codec->m_codec_data.m_decompression.opj_destroy = opj_jp2_destroy;
+ 
+         l_codec->m_codec_data.m_decompression.opj_setup_decoder =
+-            (void (*)(void *, opj_dparameters_t *)) opj_jp2_setup_decoder;
++             opj_jp2_setup_decoder;
+ 
+         l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
+-            (void (*)(void *, OPJ_BOOL)) opj_jp2_decoder_set_strict_mode;
++            opj_jp2_decoder_set_strict_mode;
+ 
+         l_codec->m_codec_data.m_decompression.opj_set_decode_area =
+-            (OPJ_BOOL(*)(void *,
+-                         opj_image_t*,
+-                         OPJ_INT32, OPJ_INT32, OPJ_INT32, OPJ_INT32,
+-                         struct opj_event_mgr *)) opj_jp2_set_decode_area;
++            opj_jp2_set_decode_area;
+ 
+         l_codec->m_codec_data.m_decompression.opj_get_decoded_tile =
+-            (OPJ_BOOL(*)(void *p_codec,
+-                         opj_stream_private_t *p_cio,
+-                         opj_image_t *p_image,
+-                         struct opj_event_mgr * p_manager,
+-                         OPJ_UINT32 tile_index)) opj_jp2_get_tile;
++            opj_jp2_get_tile;
+ 
+         l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor =
+-            (OPJ_BOOL(*)(void * p_codec,
+-                         OPJ_UINT32 res_factor,
+-                         opj_event_mgr_t * p_manager)) opj_jp2_set_decoded_resolution_factor;
++            opj_jp2_set_decoded_resolution_factor;
+ 
+         l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
+-            (OPJ_BOOL(*)(void * p_codec,
+-                         OPJ_UINT32 numcomps,
+-                         const OPJ_UINT32 * comps_indices,
+-                         struct opj_event_mgr * p_manager)) opj_jp2_set_decoded_components;
++            opj_jp2_set_decoded_components;
+ 
+-        l_codec->opj_set_threads =
+-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jp2_set_threads;
++        l_codec->opj_set_threads = opj_jp2_set_threads;
+ 
+         l_codec->m_codec = opj_jp2_create(OPJ_TRUE);
+ 
+@@ -662,41 +587,25 @@ opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT p_format)
+ 
+     switch (p_format) {
+     case OPJ_CODEC_J2K:
+-        l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *,
+-                struct opj_stream_private *,
+-                struct opj_event_mgr *)) opj_j2k_encode;
+-
+-        l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *,
+-                struct opj_stream_private *,
+-                struct opj_event_mgr *)) opj_j2k_end_compress;
+-
+-        l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *,
+-                struct opj_stream_private *,
+-                struct opj_image *,
+-                struct opj_event_mgr *)) opj_j2k_start_compress;
+-
+-        l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *,
+-                OPJ_UINT32,
+-                OPJ_BYTE*,
+-                OPJ_UINT32,
+-                struct opj_stream_private *,
+-                struct opj_event_mgr *)) opj_j2k_write_tile;
+-
+-        l_codec->m_codec_data.m_compression.opj_destroy = (void (*)(
+-                    void *)) opj_j2k_destroy;
+-
+-        l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *,
+-                opj_cparameters_t *,
+-                struct opj_image *,
+-                struct opj_event_mgr *)) opj_j2k_setup_encoder;
+-
+-        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL(
+-                    *)(void *,
+-                       const char* const*,
+-                       struct opj_event_mgr *)) opj_j2k_encoder_set_extra_options;
+-
+-        l_codec->opj_set_threads =
+-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_j2k_set_threads;
++        l_codec->m_codec_data.m_compression.opj_encode = opj_j2k_encode;
++
++        l_codec->m_codec_data.m_compression.opj_end_compress =
++            opj_j2k_end_compress;
++
++        l_codec->m_codec_data.m_compression.opj_start_compress =
++            opj_j2k_start_compress;
++
++        l_codec->m_codec_data.m_compression.opj_write_tile = opj_j2k_write_tile;
++
++        l_codec->m_codec_data.m_compression.opj_destroy = opj_j2k_destroy;
++
++        l_codec->m_codec_data.m_compression.opj_setup_encoder =
++            opj_j2k_setup_encoder;
++
++        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options =
++            opj_j2k_encoder_set_extra_options;
++
++        l_codec->opj_set_threads = opj_j2k_set_threads;
+ 
+         l_codec->m_codec = opj_j2k_create_compress();
+         if (! l_codec->m_codec) {
+@@ -708,41 +617,25 @@ opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT p_format)
+ 
+     case OPJ_CODEC_JP2:
+         /* get a JP2 decoder handle */
+-        l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *,
+-                struct opj_stream_private *,
+-                struct opj_event_mgr *)) opj_jp2_encode;
+-
+-        l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *,
+-                struct opj_stream_private *,
+-                struct opj_event_mgr *)) opj_jp2_end_compress;
+-
+-        l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *,
+-                struct opj_stream_private *,
+-                struct opj_image *,
+-                struct opj_event_mgr *))  opj_jp2_start_compress;
+-
+-        l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *,
+-                OPJ_UINT32,
+-                OPJ_BYTE*,
+-                OPJ_UINT32,
+-                struct opj_stream_private *,
+-                struct opj_event_mgr *)) opj_jp2_write_tile;
+-
+-        l_codec->m_codec_data.m_compression.opj_destroy = (void (*)(
+-                    void *)) opj_jp2_destroy;
+-
+-        l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *,
+-                opj_cparameters_t *,
+-                struct opj_image *,
+-                struct opj_event_mgr *)) opj_jp2_setup_encoder;
+-
+-        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL(
+-                    *)(void *,
+-                       const char* const*,
+-                       struct opj_event_mgr *)) opj_jp2_encoder_set_extra_options;
+-
+-        l_codec->opj_set_threads =
+-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jp2_set_threads;
++        l_codec->m_codec_data.m_compression.opj_encode = opj_jp2_encode;
++
++        l_codec->m_codec_data.m_compression.opj_end_compress =
++            opj_jp2_end_compress;
++
++        l_codec->m_codec_data.m_compression.opj_start_compress =
++            opj_jp2_start_compress;
++
++        l_codec->m_codec_data.m_compression.opj_write_tile = opj_jp2_write_tile;
++
++        l_codec->m_codec_data.m_compression.opj_destroy = opj_jp2_destroy;
++
++        l_codec->m_codec_data.m_compression.opj_setup_encoder =
++            opj_jp2_setup_encoder;
++
++        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options =
++            opj_jp2_encoder_set_extra_options;
++
++        l_codec->opj_set_threads = opj_jp2_set_threads;
+ 
+         l_codec->m_codec = opj_jp2_create(OPJ_FALSE);
+         if (! l_codec->m_codec) {
diff --git a/third_party/libopenjpeg/README.pdfium b/third_party/libopenjpeg/README.pdfium
index 9498b86..638c817 100644
--- a/third_party/libopenjpeg/README.pdfium
+++ b/third_party/libopenjpeg/README.pdfium
@@ -2,6 +2,7 @@
 URL: http://www.openjpeg.org/
 Version: 2.5.0 (also update in opj_config*)
 Security Critical: yes
+Shipped: yes
 License: 2-clause BSD
 CPEPrefix: cpe:/a:uclouvain:openjpeg:2.5.0
 
@@ -33,3 +34,4 @@
 0043-mel_init.patch: Backport fix for assertion failure in mel_init().
 0044-opj_t1_allocate_buffers.patch: Backport fix for malloc size error in opj_t1_allocate_buffers().
 0045-openjp2-j2k-replace-sprintf-calls-with-snprintf.patch: Replace sprintf with snprintf for macOS 13 SDK compatibility, from https://github.com/uclouvain/openjpeg/pull/1450.
+0046-func-ptr-mixup.patch: Prevent mixing up function pointer types.
diff --git a/third_party/libopenjpeg/j2k.c b/third_party/libopenjpeg/j2k.c
index 9b06e7e..e2e0487 100644
--- a/third_party/libopenjpeg/j2k.c
+++ b/third_party/libopenjpeg/j2k.c
@@ -6685,8 +6685,9 @@
 /* J2K / JPT decoder interface                                             */
 /* ----------------------------------------------------------------------- */
 
-void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters)
+void opj_j2k_setup_decoder(void *p_j2k, opj_dparameters_t *parameters)
 {
+    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
     if (j2k && parameters) {
         j2k->m_cp.m_specific_param.m_dec.m_layer = parameters->cp_layer;
         j2k->m_cp.m_specific_param.m_dec.m_reduce = parameters->cp_reduce;
@@ -6700,15 +6701,17 @@
     }
 }
 
-void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict)
+void opj_j2k_decoder_set_strict_mode(void *p_j2k, OPJ_BOOL strict)
 {
+    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
     if (j2k) {
         j2k->m_cp.strict = strict;
     }
 }
 
-OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
+OPJ_BOOL opj_j2k_set_threads(void *p_j2k, OPJ_UINT32 num_threads)
 {
+    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
     /* Currently we pass the thread-pool to the tcd, so we cannot re-set it */
     /* afterwards */
     if (opj_has_thread_support() && j2k->m_tcd == NULL) {
@@ -7613,11 +7616,12 @@
 }
 
 
-OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_setup_encoder(void *p_j2k,
                                opj_cparameters_t *parameters,
                                opj_image_t *image,
                                opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* j2k = (opj_j2k_t*)p_j2k;
     OPJ_UINT32 i, j, tileno, numpocs_tile;
     opj_cp_t *cp = 00;
     OPJ_UINT32 cblkw, cblkh;
@@ -7666,10 +7670,10 @@
         return OPJ_FALSE;
     }
 
-    p_j2k->m_specific_param.m_encoder.m_nb_comps = image->numcomps;
+    j2k->m_specific_param.m_encoder.m_nb_comps = image->numcomps;
 
     /* keep a link to cp so that we can destroy it later in j2k_destroy_compress */
-    cp = &(p_j2k->m_cp);
+    cp = &(j2k->m_cp);
 
     /* set default values for cp */
     cp->tw = 1;
@@ -7834,7 +7838,7 @@
     }
 
     if (OPJ_IS_CINEMA(parameters->rsiz) || OPJ_IS_IMF(parameters->rsiz)) {
-        p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
+        j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
     }
 
     /* Manage profiles and applications and set RSIZ */
@@ -8379,7 +8383,7 @@
  * -----------------------------------------------------------------------
  */
 
-OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_end_decompress(void *p_j2k,
                                 opj_stream_private_t *p_stream,
                                 opj_event_mgr_t * p_manager
                                )
@@ -8391,10 +8395,11 @@
 }
 
 OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
-                             opj_j2k_t* p_j2k,
+                             void* j2k,
                              opj_image_t** p_image,
                              opj_event_mgr_t* p_manager)
 {
+    opj_j2k_t *p_j2k = (opj_j2k_t*)j2k;
     /* preconditions */
     assert(p_j2k != 00);
     assert(p_stream != 00);
@@ -9178,8 +9183,9 @@
     return e;
 }
 
-void opj_j2k_destroy(opj_j2k_t *p_j2k)
+void opj_j2k_destroy(void *j2k)
 {
+    opj_j2k_t *p_j2k = (opj_j2k_t*)j2k;
     if (p_j2k == 00) {
         return;
     }
@@ -9518,7 +9524,7 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_read_tile_header(void * j2k,
                                   OPJ_UINT32 * p_tile_index,
                                   OPJ_UINT32 * p_data_size,
                                   OPJ_INT32 * p_tile_x0, OPJ_INT32 * p_tile_y0,
@@ -9528,6 +9534,7 @@
                                   opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 l_current_marker = J2K_MS_SOT;
     OPJ_UINT32 l_marker_size;
     const opj_dec_memory_marker_handler_t * l_marker_handler = 00;
@@ -9827,13 +9834,14 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_decode_tile(void * j2k,
                              OPJ_UINT32 p_tile_index,
                              OPJ_BYTE * p_data,
                              OPJ_UINT32 p_data_size,
                              opj_stream_private_t *p_stream,
                              opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 l_current_marker;
     OPJ_BYTE l_data [2];
     opj_tcp_t * l_tcp;
@@ -10200,11 +10208,12 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_set_decoded_components(void *j2k,
                                         OPJ_UINT32 numcomps,
                                         const OPJ_UINT32* comps_indices,
                                         opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 i;
     OPJ_BOOL* already_mapped;
 
@@ -10260,12 +10269,13 @@
 }
 
 
-OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_set_decode_area(void *j2k,
                                  opj_image_t* p_image,
                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
                                  opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     opj_cp_t * l_cp = &(p_j2k->m_cp);
     opj_image_t * l_image = p_j2k->m_private_image;
     OPJ_BOOL ret;
@@ -11200,8 +11210,9 @@
     }
 }
 
-void j2k_dump(opj_j2k_t* p_j2k, OPJ_INT32 flag, FILE* out_stream)
+void j2k_dump(void* j2k, OPJ_INT32 flag, FILE* out_stream)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     /* Check if the flag is compatible with j2k file*/
     if ((flag & OPJ_JP2_INFO) || (flag & OPJ_JP2_IND)) {
         fprintf(out_stream, "Wrong flag\n");
@@ -11391,8 +11402,9 @@
     }
 }
 
-opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k)
+opj_codestream_info_v2_t* j2k_get_cstr_info(void* j2k)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 compno;
     OPJ_UINT32 numcomps = p_j2k->m_private_image->numcomps;
     opj_tcp_t *l_default_tile;
@@ -11467,8 +11479,9 @@
     return cstr_info;
 }
 
-opj_codestream_index_t* j2k_get_cstr_index(opj_j2k_t* p_j2k)
+opj_codestream_index_t* j2k_get_cstr_index(void* j2k)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     opj_codestream_index_t* l_cstr_index = (opj_codestream_index_t*)
                                            opj_calloc(1, sizeof(opj_codestream_index_t));
     if (!l_cstr_index) {
@@ -11972,11 +11985,12 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_decode(void * j2k,
                         opj_stream_private_t * p_stream,
                         opj_image_t * p_image,
                         opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     if (!p_image) {
         return OPJ_FALSE;
     }
@@ -12030,12 +12044,13 @@
     return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
 }
 
-OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_get_tile(void *j2k,
                           opj_stream_private_t *p_stream,
                           opj_image_t* p_image,
                           opj_event_mgr_t * p_manager,
                           OPJ_UINT32 tile_index)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 compno;
     OPJ_UINT32 l_tile_x, l_tile_y;
     opj_image_comp_t* l_img_comp;
@@ -12143,10 +12158,11 @@
     return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
 }
 
-OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_set_decoded_resolution_factor(void *j2k,
         OPJ_UINT32 res_factor,
         opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 it_comp;
 
     p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor;
@@ -12177,10 +12193,11 @@
 /* ----------------------------------------------------------------------- */
 
 OPJ_BOOL opj_j2k_encoder_set_extra_options(
-    opj_j2k_t *p_j2k,
+    void *j2k,
     const char* const* p_options,
     opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     const char* const* p_option_iter;
 
     if (p_options == NULL) {
@@ -12239,10 +12256,11 @@
 
 /* ----------------------------------------------------------------------- */
 
-OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_encode(void * j2k,
                         opj_stream_private_t *p_stream,
                         opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     OPJ_UINT32 i, j;
     OPJ_UINT32 l_nb_tiles;
     OPJ_SIZE_T l_max_tile_size = 0, l_current_tile_size;
@@ -12347,10 +12365,11 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_end_compress(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_end_compress(void *j2k,
                               opj_stream_private_t *p_stream,
                               opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     /* customization of the encoding */
     if (! opj_j2k_setup_end_compress(p_j2k, p_manager)) {
         return OPJ_FALSE;
@@ -12363,11 +12382,12 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_start_compress(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_start_compress(void *j2k,
                                 opj_stream_private_t *p_stream,
                                 opj_image_t * p_image,
                                 opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     /* preconditions */
     assert(p_j2k != 00);
     assert(p_stream != 00);
@@ -13154,13 +13174,14 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_j2k_write_tile(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_write_tile(void * j2k,
                             OPJ_UINT32 p_tile_index,
                             OPJ_BYTE * p_data,
                             OPJ_UINT32 p_data_size,
                             opj_stream_private_t *p_stream,
                             opj_event_mgr_t * p_manager)
 {
+    opj_j2k_t* p_j2k = (opj_j2k_t*)j2k;
     if (! opj_j2k_pre_write_tile(p_j2k, p_tile_index, p_stream, p_manager)) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "Error while opj_j2k_pre_write_tile with tile index = %d\n", p_tile_index);
diff --git a/third_party/libopenjpeg/j2k.h b/third_party/libopenjpeg/j2k.h
index 04fba64..1d824c0 100644
--- a/third_party/libopenjpeg/j2k.h
+++ b/third_party/libopenjpeg/j2k.h
@@ -621,15 +621,15 @@
 
 /**
 Setup the decoder decoding parameters using user parameters.
-Decoding parameters are returned in j2k->cp.
-@param j2k J2K decompressor handle
+Decoding parameters are returned in p_j2k->cp.
+@param p_j2k J2K decompressor handle
 @param parameters decompression parameters
 */
-void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters);
+void opj_j2k_setup_decoder(void *p_j2k, opj_dparameters_t *parameters);
 
-void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict);
+void opj_j2k_decoder_set_strict_mode(void *j2k, OPJ_BOOL strict);
 
-OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads);
+OPJ_BOOL opj_j2k_set_threads(void *j2k, OPJ_UINT32 num_threads);
 
 /**
  * Creates a J2K compression structure
@@ -639,7 +639,7 @@
 opj_j2k_t* opj_j2k_create_compress(void);
 
 
-OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_setup_encoder(void *p_j2k,
                                opj_cparameters_t *parameters,
                                opj_image_t *image,
                                opj_event_mgr_t * p_manager);
@@ -658,7 +658,7 @@
  * Ends the decompression procedures and possibiliy add data to be read after the
  * codestream.
  */
-OPJ_BOOL opj_j2k_end_decompress(opj_j2k_t *j2k,
+OPJ_BOOL opj_j2k_end_decompress(void *j2k,
                                 opj_stream_private_t *p_stream,
                                 opj_event_mgr_t * p_manager);
 
@@ -666,14 +666,14 @@
  * Reads a jpeg2000 codestream header structure.
  *
  * @param p_stream the stream to read data from.
- * @param p_j2k the jpeg2000 codec.
+ * @param j2k the jpeg2000 codec.
  * @param p_image FIXME DOC
  * @param p_manager the user event manager.
  *
  * @return true if the box is valid.
  */
 OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
-                             opj_j2k_t* p_j2k,
+                             void* j2k,
                              opj_image_t** p_image,
                              opj_event_mgr_t* p_manager);
 
@@ -681,9 +681,9 @@
 /**
  * Destroys a jpeg2000 codec.
  *
- * @param   p_j2k   the jpeg20000 structure to destroy.
+ * @param   j2k   the jpeg20000 structure to destroy.
  */
-void opj_j2k_destroy(opj_j2k_t *p_j2k);
+void opj_j2k_destroy(void *j2k);
 
 /**
  * Destroys a codestream index structure.
@@ -694,14 +694,14 @@
 
 /**
  * Decode tile data.
- * @param   p_j2k       the jpeg2000 codec.
+ * @param   j2k       the jpeg2000 codec.
  * @param   p_tile_index
  * @param p_data       FIXME DOC
  * @param p_data_size  FIXME DOC
  * @param   p_stream            the stream to write data to.
  * @param   p_manager   the user event manager.
  */
-OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_decode_tile(void * j2k,
                              OPJ_UINT32 p_tile_index,
                              OPJ_BYTE * p_data,
                              OPJ_UINT32 p_data_size,
@@ -710,7 +710,7 @@
 
 /**
  * Reads a tile header.
- * @param   p_j2k       the jpeg2000 codec.
+ * @param   j2k       the jpeg2000 codec.
  * @param   p_tile_index FIXME DOC
  * @param   p_data_size FIXME DOC
  * @param   p_tile_x0 FIXME DOC
@@ -722,7 +722,7 @@
  * @param   p_stream            the stream to write data to.
  * @param   p_manager   the user event manager.
  */
-OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_read_tile_header(void * j2k,
                                   OPJ_UINT32 * p_tile_index,
                                   OPJ_UINT32 * p_data_size,
                                   OPJ_INT32 * p_tile_x0,
@@ -737,7 +737,7 @@
 
 /** Sets the indices of the components to decode.
  *
- * @param p_j2k         the jpeg2000 codec.
+ * @param j2k         the jpeg2000 codec.
  * @param numcomps      Number of components to decode.
  * @param comps_indices Array of num_compts indices (numbering starting at 0)
  *                      corresponding to the components to decode.
@@ -745,7 +745,7 @@
  *
  * @return OPJ_TRUE in case of success.
  */
-OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_set_decoded_components(void *j2k,
                                         OPJ_UINT32 numcomps,
                                         const OPJ_UINT32* comps_indices,
                                         opj_event_mgr_t * p_manager);
@@ -753,7 +753,7 @@
 /**
  * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
  *
- * @param   p_j2k           the jpeg2000 codec.
+ * @param   j2k           the jpeg2000 codec.
  * @param   p_image     FIXME DOC
  * @param   p_start_x       the left position of the rectangle to decode (in image coordinates).
  * @param   p_start_y       the up position of the rectangle to decode (in image coordinates).
@@ -763,7 +763,7 @@
  *
  * @return  true            if the area could be set.
  */
-OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_set_decode_area(void *j2k,
                                  opj_image_t* p_image,
                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
@@ -780,12 +780,12 @@
 /**
  * Dump some elements from the J2K decompression structure .
  *
- *@param p_j2k              the jpeg2000 codec.
+ *@param j2k                the jpeg2000 codec.
  *@param flag               flag to describe what elements are dump.
  *@param out_stream         output stream where dump the elements.
  *
 */
-void j2k_dump(opj_j2k_t* p_j2k, OPJ_INT32 flag, FILE* out_stream);
+void j2k_dump(void* j2k, OPJ_INT32 flag, FILE* out_stream);
 
 
 
@@ -812,20 +812,20 @@
 /**
  * Get the codestream info from a JPEG2000 codec.
  *
- *@param    p_j2k               the component image header to dump.
+ *@param    j2k               the component image header to dump.
  *
  *@return   the codestream information extract from the jpg2000 codec
  */
-opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k);
+opj_codestream_info_v2_t* j2k_get_cstr_info(void* j2k);
 
 /**
  * Get the codestream index from a JPEG2000 codec.
  *
- *@param    p_j2k               the component image header to dump.
+ *@param    j2k               the component image header to dump.
  *
  *@return   the codestream index extract from the jpg2000 codec
  */
-opj_codestream_index_t* j2k_get_cstr_index(opj_j2k_t* p_j2k);
+opj_codestream_index_t* j2k_get_cstr_index(void* j2k);
 
 /**
  * Decode an image from a JPEG-2000 codestream
@@ -835,46 +835,46 @@
  * @param p_manager FIXME DOC
  * @return FIXME DOC
 */
-OPJ_BOOL opj_j2k_decode(opj_j2k_t *j2k,
+OPJ_BOOL opj_j2k_decode(void *j2k,
                         opj_stream_private_t *p_stream,
                         opj_image_t *p_image,
                         opj_event_mgr_t *p_manager);
 
 
-OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_get_tile(void *j2k,
                           opj_stream_private_t *p_stream,
                           opj_image_t* p_image,
                           opj_event_mgr_t * p_manager,
                           OPJ_UINT32 tile_index);
 
-OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_set_decoded_resolution_factor(void *j2k,
         OPJ_UINT32 res_factor,
         opj_event_mgr_t * p_manager);
 
 /**
  * Specify extra options for the encoder.
  *
- * @param  p_j2k        the jpeg2000 codec.
+ * @param  j2k          the jpeg2000 codec.
  * @param  p_options    options
  * @param  p_manager    the user event manager
  *
  * @see opj_encoder_set_extra_options() for more details.
  */
 OPJ_BOOL opj_j2k_encoder_set_extra_options(
-    opj_j2k_t *p_j2k,
+    void *j2k,
     const char* const* p_options,
     opj_event_mgr_t * p_manager);
 
 /**
  * Writes a tile.
- * @param   p_j2k       the jpeg2000 codec.
+ * @param   j2k         the jpeg2000 codec.
  * @param p_tile_index FIXME DOC
  * @param p_data FIXME DOC
  * @param p_data_size FIXME DOC
  * @param   p_stream            the stream to write data to.
  * @param   p_manager   the user event manager.
  */
-OPJ_BOOL opj_j2k_write_tile(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_write_tile(void * j2k,
                             OPJ_UINT32 p_tile_index,
                             OPJ_BYTE * p_data,
                             OPJ_UINT32 p_data_size,
@@ -884,21 +884,21 @@
 /**
  * Encodes an image into a JPEG-2000 codestream
  */
-OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
+OPJ_BOOL opj_j2k_encode(void * j2k,
                         opj_stream_private_t *cio,
                         opj_event_mgr_t * p_manager);
 
 /**
  * Starts a compression scheme, i.e. validates the codec parameters, writes the header.
  *
- * @param   p_j2k       the jpeg2000 codec.
+ * @param   j2k         the jpeg2000 codec.
  * @param   p_stream            the stream object.
  * @param   p_image FIXME DOC
  * @param   p_manager   the user event manager.
  *
  * @return true if the codec is valid.
  */
-OPJ_BOOL opj_j2k_start_compress(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_start_compress(void *j2k,
                                 opj_stream_private_t *p_stream,
                                 opj_image_t * p_image,
                                 opj_event_mgr_t * p_manager);
@@ -907,7 +907,7 @@
  * Ends the compression procedures and possibiliy add data to be read after the
  * codestream.
  */
-OPJ_BOOL opj_j2k_end_compress(opj_j2k_t *p_j2k,
+OPJ_BOOL opj_j2k_end_compress(void *j2k,
                               opj_stream_private_t *cio,
                               opj_event_mgr_t * p_manager);
 
diff --git a/third_party/libopenjpeg/jp2.c b/third_party/libopenjpeg/jp2.c
index 44d0c98..6db728d 100644
--- a/third_party/libopenjpeg/jp2.c
+++ b/third_party/libopenjpeg/jp2.c
@@ -1609,11 +1609,12 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_decode(void *p_jp2,
                         opj_stream_private_t *p_stream,
                         opj_image_t* p_image,
                         opj_event_mgr_t * p_manager)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     if (!p_image) {
         return OPJ_FALSE;
     }
@@ -1905,8 +1906,9 @@
 /* JP2 decoder interface                                             */
 /* ----------------------------------------------------------------------- */
 
-void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters)
+void opj_jp2_setup_decoder(void *p_jp2, opj_dparameters_t *parameters)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     /* setup the J2K codec */
     opj_j2k_setup_decoder(jp2->j2k, parameters);
 
@@ -1917,13 +1919,15 @@
                                  OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
 }
 
-void opj_jp2_decoder_set_strict_mode(opj_jp2_t *jp2, OPJ_BOOL strict)
+void opj_jp2_decoder_set_strict_mode(void *p_jp2, OPJ_BOOL strict)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     opj_j2k_decoder_set_strict_mode(jp2->j2k, strict);
 }
 
-OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads)
+OPJ_BOOL opj_jp2_set_threads(void *p_jp2, OPJ_UINT32 num_threads)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     return opj_j2k_set_threads(jp2->j2k, num_threads);
 }
 
@@ -1931,11 +1935,12 @@
 /* JP2 encoder interface                                             */
 /* ----------------------------------------------------------------------- */
 
-OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_setup_encoder(void *p_jp2,
                                opj_cparameters_t *parameters,
                                opj_image_t *image,
                                opj_event_mgr_t * p_manager)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     OPJ_UINT32 i;
     OPJ_UINT32 depth_0;
     OPJ_UINT32 sign;
@@ -2118,18 +2123,20 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_jp2_encode(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_encode(void *p_jp2,
                         opj_stream_private_t *stream,
                         opj_event_mgr_t * p_manager)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     return opj_j2k_encode(jp2->j2k, stream, p_manager);
 }
 
-OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_end_decompress(void *p_jp2,
                                 opj_stream_private_t *cio,
                                 opj_event_mgr_t * p_manager
                                )
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     /* preconditions */
     assert(jp2 != 00);
     assert(cio != 00);
@@ -2148,11 +2155,12 @@
     return opj_j2k_end_decompress(jp2->j2k, cio, p_manager);
 }
 
-OPJ_BOOL opj_jp2_end_compress(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_end_compress(void *p_jp2,
                               opj_stream_private_t *cio,
                               opj_event_mgr_t * p_manager
                              )
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     /* preconditions */
     assert(jp2 != 00);
     assert(cio != 00);
@@ -2476,12 +2484,13 @@
     return l_result;
 }
 
-OPJ_BOOL opj_jp2_start_compress(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_start_compress(void *p_jp2,
                                 opj_stream_private_t *stream,
                                 opj_image_t * p_image,
                                 opj_event_mgr_t * p_manager
                                )
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     /* preconditions */
     assert(jp2 != 00);
     assert(stream != 00);
@@ -2854,11 +2863,12 @@
 }
 
 OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
-                             opj_jp2_t *jp2,
+                             void *p_jp2,
                              opj_image_t ** p_image,
                              opj_event_mgr_t * p_manager
                             )
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     /* preconditions */
     assert(jp2 != 00);
     assert(p_stream != 00);
@@ -2981,7 +2991,7 @@
     return OPJ_TRUE;
 }
 
-OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
+OPJ_BOOL opj_jp2_read_tile_header(void *p_jp2,
                                   OPJ_UINT32 * p_tile_index,
                                   OPJ_UINT32 * p_data_size,
                                   OPJ_INT32 * p_tile_x0,
@@ -2994,7 +3004,8 @@
                                   opj_event_mgr_t * p_manager
                                  )
 {
-    return opj_j2k_read_tile_header(p_jp2->j2k,
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_read_tile_header(jp2->j2k,
                                     p_tile_index,
                                     p_data_size,
                                     p_tile_x0, p_tile_y0,
@@ -3005,7 +3016,7 @@
                                     p_manager);
 }
 
-OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_write_tile(void *p_jp2,
                             OPJ_UINT32 p_tile_index,
                             OPJ_BYTE * p_data,
                             OPJ_UINT32 p_data_size,
@@ -3014,11 +3025,12 @@
                            )
 
 {
-    return opj_j2k_write_tile(p_jp2->j2k, p_tile_index, p_data, p_data_size,
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_write_tile(jp2->j2k, p_tile_index, p_data, p_data_size,
                               p_stream, p_manager);
 }
 
-OPJ_BOOL opj_jp2_decode_tile(opj_jp2_t * p_jp2,
+OPJ_BOOL opj_jp2_decode_tile(void *p_jp2,
                              OPJ_UINT32 p_tile_index,
                              OPJ_BYTE * p_data,
                              OPJ_UINT32 p_data_size,
@@ -3026,12 +3038,14 @@
                              opj_event_mgr_t * p_manager
                             )
 {
-    return opj_j2k_decode_tile(p_jp2->j2k, p_tile_index, p_data, p_data_size,
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_decode_tile(jp2->j2k, p_tile_index, p_data, p_data_size,
                                p_stream, p_manager);
 }
 
-void opj_jp2_destroy(opj_jp2_t *jp2)
+void opj_jp2_destroy(void *p_jp2)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     if (jp2) {
         /* destroy the J2K codec */
         opj_j2k_destroy(jp2->j2k);
@@ -3098,34 +3112,37 @@
     }
 }
 
-OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_set_decoded_components(void *p_jp2,
                                         OPJ_UINT32 numcomps,
                                         const OPJ_UINT32* comps_indices,
                                         opj_event_mgr_t * p_manager)
 {
-    return opj_j2k_set_decoded_components(p_jp2->j2k,
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_set_decoded_components(jp2->j2k,
                                           numcomps, comps_indices,
                                           p_manager);
 }
 
-OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_set_decode_area(void *p_jp2,
                                  opj_image_t* p_image,
                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
                                  opj_event_mgr_t * p_manager
                                 )
 {
-    return opj_j2k_set_decode_area(p_jp2->j2k, p_image, p_start_x, p_start_y,
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_set_decode_area(jp2->j2k, p_image, p_start_x, p_start_y,
                                    p_end_x, p_end_y, p_manager);
 }
 
-OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_get_tile(void *jp2,
                           opj_stream_private_t *p_stream,
                           opj_image_t* p_image,
                           opj_event_mgr_t * p_manager,
                           OPJ_UINT32 tile_index
                          )
 {
+    opj_jp2_t *p_jp2 = (opj_jp2_t*)jp2;
     if (!p_image) {
         return OPJ_FALSE;
     }
@@ -3234,41 +3251,46 @@
     return jp2;
 }
 
-void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream)
+void jp2_dump(void* p_jp2, OPJ_INT32 flag, FILE* out_stream)
 {
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
     /* preconditions */
     assert(p_jp2 != 00);
 
-    j2k_dump(p_jp2->j2k,
+    j2k_dump(jp2->j2k,
              flag,
              out_stream);
 }
 
-opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2)
+opj_codestream_index_t* jp2_get_cstr_index(void* p_jp2)
 {
-    return j2k_get_cstr_index(p_jp2->j2k);
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return j2k_get_cstr_index(jp2->j2k);
 }
 
-opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2)
+opj_codestream_info_v2_t* jp2_get_cstr_info(void* p_jp2)
 {
-    return j2k_get_cstr_info(p_jp2->j2k);
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return j2k_get_cstr_info(jp2->j2k);
 }
 
-OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_set_decoded_resolution_factor(void *p_jp2,
         OPJ_UINT32 res_factor,
         opj_event_mgr_t * p_manager)
 {
-    return opj_j2k_set_decoded_resolution_factor(p_jp2->j2k, res_factor, p_manager);
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_set_decoded_resolution_factor(jp2->j2k, res_factor, p_manager);
 }
 
 /* ----------------------------------------------------------------------- */
 
 OPJ_BOOL opj_jp2_encoder_set_extra_options(
-    opj_jp2_t *p_jp2,
+    void *p_jp2,
     const char* const* p_options,
     opj_event_mgr_t * p_manager)
 {
-    return opj_j2k_encoder_set_extra_options(p_jp2->j2k, p_options, p_manager);
+    opj_jp2_t *jp2 = (opj_jp2_t*)p_jp2;
+    return opj_j2k_encoder_set_extra_options(jp2->j2k, p_options, p_manager);
 }
 
 /* ----------------------------------------------------------------------- */
diff --git a/third_party/libopenjpeg/jp2.h b/third_party/libopenjpeg/jp2.h
index 173f251..fd9175a 100644
--- a/third_party/libopenjpeg/jp2.h
+++ b/third_party/libopenjpeg/jp2.h
@@ -230,38 +230,38 @@
 /**
 Setup the decoder decoding parameters using user parameters.
 Decoding parameters are returned in jp2->j2k->cp.
-@param jp2 JP2 decompressor handle
+@param p_jp2 JP2 decompressor handle
 @param parameters decompression parameters
 */
-void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters);
+void opj_jp2_setup_decoder(void *p_jp2, opj_dparameters_t *parameters);
 
 /**
 Set the strict mode parameter.  When strict mode is enabled, the entire
 bitstream must be decoded or an error is returned.  When it is disabled,
 the decoder will decode partial bitstreams.
-@param jp2 JP2 decompressor handle
+@param p_jp2 JP2 decompressor handle
 @param strict OPJ_TRUE for strict mode
 */
-void opj_jp2_decoder_set_strict_mode(opj_jp2_t *jp2, OPJ_BOOL strict);
+void opj_jp2_decoder_set_strict_mode(void *p_jp2, OPJ_BOOL strict);
 
 /** Allocates worker threads for the compressor/decompressor.
  *
- * @param jp2 JP2 decompressor handle
+ * @param p_jp2 JP2 decompressor handle
  * @param num_threads Number of threads.
  * @return OPJ_TRUE in case of success.
  */
-OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads);
+OPJ_BOOL opj_jp2_set_threads(void *p_jp2, OPJ_UINT32 num_threads);
 
 /**
  * Decode an image from a JPEG-2000 file stream
- * @param jp2 JP2 decompressor handle
+ * @param p_jp2 JP2 decompressor handle
  * @param p_stream  FIXME DOC
  * @param p_image   FIXME DOC
  * @param p_manager FIXME DOC
  *
  * @return Returns a decoded image if successful, returns NULL otherwise
 */
-OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_decode(void *p_jp2,
                         opj_stream_private_t *p_stream,
                         opj_image_t* p_image,
                         opj_event_mgr_t * p_manager);
@@ -270,25 +270,25 @@
  * Setup the encoder parameters using the current image and using user parameters.
  * Coding parameters are returned in jp2->j2k->cp.
  *
- * @param jp2 JP2 compressor handle
+ * @param p_jp2 JP2 compressor handle
  * @param parameters compression parameters
  * @param image input filled image
  * @param p_manager  FIXME DOC
  * @return OPJ_TRUE if successful, OPJ_FALSE otherwise
 */
-OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_setup_encoder(void *p_jp2,
                                opj_cparameters_t *parameters,
                                opj_image_t *image,
                                opj_event_mgr_t * p_manager);
 
 /**
 Encode an image into a JPEG-2000 file stream
-@param jp2      JP2 compressor handle
+@param p_jp2      JP2 compressor handle
 @param stream    Output buffer stream
 @param p_manager  event manager
 @return Returns true if successful, returns false otherwise
 */
-OPJ_BOOL opj_jp2_encode(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_encode(void *p_jp2,
                         opj_stream_private_t *stream,
                         opj_event_mgr_t * p_manager);
 
@@ -296,14 +296,14 @@
 /**
  * Starts a compression scheme, i.e. validates the codec parameters, writes the header.
  *
- * @param  jp2    the jpeg2000 file codec.
+ * @param  p_jp2    the jpeg2000 file codec.
  * @param  stream    the stream object.
  * @param  p_image   FIXME DOC
  * @param p_manager FIXME DOC
  *
  * @return true if the codec is valid.
  */
-OPJ_BOOL opj_jp2_start_compress(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_start_compress(void *p_jp2,
                                 opj_stream_private_t *stream,
                                 opj_image_t * p_image,
                                 opj_event_mgr_t * p_manager);
@@ -313,7 +313,7 @@
  * Ends the compression procedures and possibiliy add data to be read after the
  * codestream.
  */
-OPJ_BOOL opj_jp2_end_compress(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_end_compress(void *p_jp2,
                               opj_stream_private_t *cio,
                               opj_event_mgr_t * p_manager);
 
@@ -323,7 +323,7 @@
  * Ends the decompression procedures and possibiliy add data to be read after the
  * codestream.
  */
-OPJ_BOOL opj_jp2_end_decompress(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_end_decompress(void *p_jp2,
                                 opj_stream_private_t *cio,
                                 opj_event_mgr_t * p_manager);
 
@@ -331,20 +331,20 @@
  * Reads a jpeg2000 file header structure.
  *
  * @param p_stream the stream to read data from.
- * @param jp2 the jpeg2000 file header structure.
+ * @param p_jp2 the jpeg2000 file header structure.
  * @param p_image   FIXME DOC
  * @param p_manager the user event manager.
  *
  * @return true if the box is valid.
  */
 OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
-                             opj_jp2_t *jp2,
+                             void *p_jp2,
                              opj_image_t ** p_image,
                              opj_event_mgr_t * p_manager);
 
 /** Sets the indices of the components to decode.
  *
- * @param jp2 JP2 decompressor handle
+ * @param p_jp2 JP2 decompressor handle
  * @param numcomps Number of components to decode.
  * @param comps_indices Array of num_compts indices (numbering starting at 0)
  *                     corresponding to the components to decode.
@@ -352,7 +352,7 @@
  *
  * @return OPJ_TRUE in case of success.
  */
-OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *jp2,
+OPJ_BOOL opj_jp2_set_decoded_components(void *p_jp2,
                                         OPJ_UINT32 numcomps,
                                         const OPJ_UINT32* comps_indices,
                                         opj_event_mgr_t * p_manager);
@@ -371,7 +371,7 @@
  * @param  p_stream      the stream to write data to.
  * @param  p_manager     the user event manager.
  */
-OPJ_BOOL opj_jp2_read_tile_header(opj_jp2_t * p_jp2,
+OPJ_BOOL opj_jp2_read_tile_header(void * p_jp2,
                                   OPJ_UINT32 * p_tile_index,
                                   OPJ_UINT32 * p_data_size,
                                   OPJ_INT32 * p_tile_x0,
@@ -393,7 +393,7 @@
  * @param  p_stream      the stream to write data to.
  * @param  p_manager  the user event manager.
  */
-OPJ_BOOL opj_jp2_write_tile(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_write_tile(void *p_jp2,
                             OPJ_UINT32 p_tile_index,
                             OPJ_BYTE * p_data,
                             OPJ_UINT32 p_data_size,
@@ -411,7 +411,7 @@
  *
  * @return FIXME DOC
  */
-OPJ_BOOL opj_jp2_decode_tile(opj_jp2_t * p_jp2,
+OPJ_BOOL opj_jp2_decode_tile(void * p_jp2,
                              OPJ_UINT32 p_tile_index,
                              OPJ_BYTE * p_data,
                              OPJ_UINT32 p_data_size,
@@ -427,9 +427,9 @@
 
 /**
 Destroy a JP2 decompressor handle
-@param jp2 JP2 decompressor handle to destroy
+@param p_jp2 JP2 decompressor handle to destroy
 */
-void opj_jp2_destroy(opj_jp2_t *jp2);
+void opj_jp2_destroy(void *p_jp2);
 
 
 /**
@@ -445,7 +445,7 @@
  *
  * @return  true      if the area could be set.
  */
-OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_set_decode_area(void *p_jp2,
                                  opj_image_t* p_image,
                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
                                  OPJ_INT32 p_end_x, OPJ_INT32 p_end_y,
@@ -454,7 +454,7 @@
 /**
 *
 */
-OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_get_tile(void *jp2,
                           opj_stream_private_t *p_stream,
                           opj_image_t* p_image,
                           opj_event_mgr_t * p_manager,
@@ -464,7 +464,7 @@
 /**
  *
  */
-OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2,
+OPJ_BOOL opj_jp2_set_decoded_resolution_factor(void *p_jp2,
         OPJ_UINT32 res_factor,
         opj_event_mgr_t * p_manager);
 
@@ -478,7 +478,7 @@
  * @see opj_encoder_set_extra_options() for more details.
  */
 OPJ_BOOL opj_jp2_encoder_set_extra_options(
-    opj_jp2_t *p_jp2,
+    void *p_jp2,
     const char* const* p_options,
     opj_event_mgr_t * p_manager);
 
@@ -492,7 +492,7 @@
  *@param out_stream      output stream where dump the elements.
  *
 */
-void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream);
+void jp2_dump(void* p_jp2, OPJ_INT32 flag, FILE* out_stream);
 
 /**
  * Get the codestream info from a JPEG2000 codec.
@@ -501,7 +501,7 @@
  *
  *@return  the codestream information extract from the jpg2000 codec
  */
-opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2);
+opj_codestream_info_v2_t* jp2_get_cstr_info(void* p_jp2);
 
 /**
  * Get the codestream index from a JPEG2000 codec.
@@ -510,7 +510,7 @@
  *
  *@return  the codestream index extract from the jpg2000 codec
  */
-opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2);
+opj_codestream_index_t* jp2_get_cstr_index(void* p_jp2);
 
 
 /*@}*/
diff --git a/third_party/libopenjpeg/openjpeg.c b/third_party/libopenjpeg/openjpeg.c
index 29d3ee5..9dd4256 100644
--- a/third_party/libopenjpeg/openjpeg.c
+++ b/third_party/libopenjpeg/openjpeg.c
@@ -189,85 +189,48 @@
 
     switch (p_format) {
     case OPJ_CODEC_J2K:
-        l_codec->opj_dump_codec = (void (*)(void*, OPJ_INT32, FILE*)) j2k_dump;
+        l_codec->opj_dump_codec = j2k_dump;
 
-        l_codec->opj_get_codec_info = (opj_codestream_info_v2_t* (*)(
-                                           void*)) j2k_get_cstr_info;
+        l_codec->opj_get_codec_info = j2k_get_cstr_info;
 
-        l_codec->opj_get_codec_index = (opj_codestream_index_t* (*)(
-                                            void*)) j2k_get_cstr_index;
+        l_codec->opj_get_codec_index = j2k_get_cstr_index;
 
-        l_codec->m_codec_data.m_decompression.opj_decode =
-            (OPJ_BOOL(*)(void *,
-                         struct opj_stream_private *,
-                         opj_image_t*, struct opj_event_mgr *)) opj_j2k_decode;
+        l_codec->m_codec_data.m_decompression.opj_decode = opj_j2k_decode;
 
         l_codec->m_codec_data.m_decompression.opj_end_decompress =
-            (OPJ_BOOL(*)(void *,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) opj_j2k_end_decompress;
+            opj_j2k_end_decompress;
 
         l_codec->m_codec_data.m_decompression.opj_read_header =
-            (OPJ_BOOL(*)(struct opj_stream_private *,
-                         void *,
-                         opj_image_t **,
-                         struct opj_event_mgr *)) opj_j2k_read_header;
+            opj_j2k_read_header;
 
-        l_codec->m_codec_data.m_decompression.opj_destroy =
-            (void (*)(void *))opj_j2k_destroy;
+        l_codec->m_codec_data.m_decompression.opj_destroy = opj_j2k_destroy;
 
         l_codec->m_codec_data.m_decompression.opj_setup_decoder =
-            (void (*)(void *, opj_dparameters_t *)) opj_j2k_setup_decoder;
+            opj_j2k_setup_decoder;
 
         l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
-            (void (*)(void *, OPJ_BOOL)) opj_j2k_decoder_set_strict_mode;
+            opj_j2k_decoder_set_strict_mode;
 
 
         l_codec->m_codec_data.m_decompression.opj_read_tile_header =
-            (OPJ_BOOL(*)(void *,
-                         OPJ_UINT32*,
-                         OPJ_UINT32*,
-                         OPJ_INT32*, OPJ_INT32*,
-                         OPJ_INT32*, OPJ_INT32*,
-                         OPJ_UINT32*,
-                         OPJ_BOOL*,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) opj_j2k_read_tile_header;
+            opj_j2k_read_tile_header;
 
         l_codec->m_codec_data.m_decompression.opj_decode_tile_data =
-            (OPJ_BOOL(*)(void *,
-                         OPJ_UINT32,
-                         OPJ_BYTE*,
-                         OPJ_UINT32,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) opj_j2k_decode_tile;
+            opj_j2k_decode_tile;
 
         l_codec->m_codec_data.m_decompression.opj_set_decode_area =
-            (OPJ_BOOL(*)(void *,
-                         opj_image_t*,
-                         OPJ_INT32, OPJ_INT32, OPJ_INT32, OPJ_INT32,
-                         struct opj_event_mgr *)) opj_j2k_set_decode_area;
+            opj_j2k_set_decode_area;
 
         l_codec->m_codec_data.m_decompression.opj_get_decoded_tile =
-            (OPJ_BOOL(*)(void *p_codec,
-                         opj_stream_private_t *p_cio,
-                         opj_image_t *p_image,
-                         struct opj_event_mgr * p_manager,
-                         OPJ_UINT32 tile_index)) opj_j2k_get_tile;
+            opj_j2k_get_tile;
 
         l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor =
-            (OPJ_BOOL(*)(void * p_codec,
-                         OPJ_UINT32 res_factor,
-                         struct opj_event_mgr * p_manager)) opj_j2k_set_decoded_resolution_factor;
+            opj_j2k_set_decoded_resolution_factor;
 
         l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
-            (OPJ_BOOL(*)(void * p_codec,
-                         OPJ_UINT32 numcomps,
-                         const OPJ_UINT32 * comps_indices,
-                         struct opj_event_mgr * p_manager)) opj_j2k_set_decoded_components;
+            opj_j2k_set_decoded_components;
 
-        l_codec->opj_set_threads =
-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_j2k_set_threads;
+        l_codec->opj_set_threads = opj_j2k_set_threads;
 
         l_codec->m_codec = opj_j2k_create_decompress();
 
@@ -280,85 +243,47 @@
 
     case OPJ_CODEC_JP2:
         /* get a JP2 decoder handle */
-        l_codec->opj_dump_codec = (void (*)(void*, OPJ_INT32, FILE*)) jp2_dump;
+        l_codec->opj_dump_codec = jp2_dump;
 
-        l_codec->opj_get_codec_info = (opj_codestream_info_v2_t* (*)(
-                                           void*)) jp2_get_cstr_info;
+        l_codec->opj_get_codec_info = jp2_get_cstr_info;
 
-        l_codec->opj_get_codec_index = (opj_codestream_index_t* (*)(
-                                            void*)) jp2_get_cstr_index;
+        l_codec->opj_get_codec_index = jp2_get_cstr_index;
 
-        l_codec->m_codec_data.m_decompression.opj_decode =
-            (OPJ_BOOL(*)(void *,
-                         struct opj_stream_private *,
-                         opj_image_t*,
-                         struct opj_event_mgr *)) opj_jp2_decode;
+        l_codec->m_codec_data.m_decompression.opj_decode = opj_jp2_decode;
 
         l_codec->m_codec_data.m_decompression.opj_end_decompress =
-            (OPJ_BOOL(*)(void *,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) opj_jp2_end_decompress;
+            opj_jp2_end_decompress;
 
         l_codec->m_codec_data.m_decompression.opj_read_header =
-            (OPJ_BOOL(*)(struct opj_stream_private *,
-                         void *,
-                         opj_image_t **,
-                         struct opj_event_mgr *)) opj_jp2_read_header;
+            opj_jp2_read_header;
 
         l_codec->m_codec_data.m_decompression.opj_read_tile_header =
-            (OPJ_BOOL(*)(void *,
-                         OPJ_UINT32*,
-                         OPJ_UINT32*,
-                         OPJ_INT32*,
-                         OPJ_INT32*,
-                         OPJ_INT32 *,
-                         OPJ_INT32 *,
-                         OPJ_UINT32 *,
-                         OPJ_BOOL *,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) opj_jp2_read_tile_header;
+            opj_jp2_read_tile_header;
 
         l_codec->m_codec_data.m_decompression.opj_decode_tile_data =
-            (OPJ_BOOL(*)(void *,
-                         OPJ_UINT32, OPJ_BYTE*, OPJ_UINT32,
-                         struct opj_stream_private *,
-                         struct opj_event_mgr *)) opj_jp2_decode_tile;
+            opj_jp2_decode_tile;
 
-        l_codec->m_codec_data.m_decompression.opj_destroy = (void (*)(
-                    void *))opj_jp2_destroy;
+        l_codec->m_codec_data.m_decompression.opj_destroy = opj_jp2_destroy;
 
         l_codec->m_codec_data.m_decompression.opj_setup_decoder =
-            (void (*)(void *, opj_dparameters_t *)) opj_jp2_setup_decoder;
+             opj_jp2_setup_decoder;
 
         l_codec->m_codec_data.m_decompression.opj_decoder_set_strict_mode =
-            (void (*)(void *, OPJ_BOOL)) opj_jp2_decoder_set_strict_mode;
+            opj_jp2_decoder_set_strict_mode;
 
         l_codec->m_codec_data.m_decompression.opj_set_decode_area =
-            (OPJ_BOOL(*)(void *,
-                         opj_image_t*,
-                         OPJ_INT32, OPJ_INT32, OPJ_INT32, OPJ_INT32,
-                         struct opj_event_mgr *)) opj_jp2_set_decode_area;
+            opj_jp2_set_decode_area;
 
         l_codec->m_codec_data.m_decompression.opj_get_decoded_tile =
-            (OPJ_BOOL(*)(void *p_codec,
-                         opj_stream_private_t *p_cio,
-                         opj_image_t *p_image,
-                         struct opj_event_mgr * p_manager,
-                         OPJ_UINT32 tile_index)) opj_jp2_get_tile;
+            opj_jp2_get_tile;
 
         l_codec->m_codec_data.m_decompression.opj_set_decoded_resolution_factor =
-            (OPJ_BOOL(*)(void * p_codec,
-                         OPJ_UINT32 res_factor,
-                         opj_event_mgr_t * p_manager)) opj_jp2_set_decoded_resolution_factor;
+            opj_jp2_set_decoded_resolution_factor;
 
         l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
-            (OPJ_BOOL(*)(void * p_codec,
-                         OPJ_UINT32 numcomps,
-                         const OPJ_UINT32 * comps_indices,
-                         struct opj_event_mgr * p_manager)) opj_jp2_set_decoded_components;
+            opj_jp2_set_decoded_components;
 
-        l_codec->opj_set_threads =
-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jp2_set_threads;
+        l_codec->opj_set_threads = opj_jp2_set_threads;
 
         l_codec->m_codec = opj_jp2_create(OPJ_TRUE);
 
@@ -662,41 +587,25 @@
 
     switch (p_format) {
     case OPJ_CODEC_J2K:
-        l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *,
-                struct opj_stream_private *,
-                struct opj_event_mgr *)) opj_j2k_encode;
+        l_codec->m_codec_data.m_compression.opj_encode = opj_j2k_encode;
 
-        l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *,
-                struct opj_stream_private *,
-                struct opj_event_mgr *)) opj_j2k_end_compress;
+        l_codec->m_codec_data.m_compression.opj_end_compress =
+            opj_j2k_end_compress;
 
-        l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *,
-                struct opj_stream_private *,
-                struct opj_image *,
-                struct opj_event_mgr *)) opj_j2k_start_compress;
+        l_codec->m_codec_data.m_compression.opj_start_compress =
+            opj_j2k_start_compress;
 
-        l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *,
-                OPJ_UINT32,
-                OPJ_BYTE*,
-                OPJ_UINT32,
-                struct opj_stream_private *,
-                struct opj_event_mgr *)) opj_j2k_write_tile;
+        l_codec->m_codec_data.m_compression.opj_write_tile = opj_j2k_write_tile;
 
-        l_codec->m_codec_data.m_compression.opj_destroy = (void (*)(
-                    void *)) opj_j2k_destroy;
+        l_codec->m_codec_data.m_compression.opj_destroy = opj_j2k_destroy;
 
-        l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *,
-                opj_cparameters_t *,
-                struct opj_image *,
-                struct opj_event_mgr *)) opj_j2k_setup_encoder;
+        l_codec->m_codec_data.m_compression.opj_setup_encoder =
+            opj_j2k_setup_encoder;
 
-        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL(
-                    *)(void *,
-                       const char* const*,
-                       struct opj_event_mgr *)) opj_j2k_encoder_set_extra_options;
+        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options =
+            opj_j2k_encoder_set_extra_options;
 
-        l_codec->opj_set_threads =
-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_j2k_set_threads;
+        l_codec->opj_set_threads = opj_j2k_set_threads;
 
         l_codec->m_codec = opj_j2k_create_compress();
         if (! l_codec->m_codec) {
@@ -708,41 +617,25 @@
 
     case OPJ_CODEC_JP2:
         /* get a JP2 decoder handle */
-        l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *,
-                struct opj_stream_private *,
-                struct opj_event_mgr *)) opj_jp2_encode;
+        l_codec->m_codec_data.m_compression.opj_encode = opj_jp2_encode;
 
-        l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *,
-                struct opj_stream_private *,
-                struct opj_event_mgr *)) opj_jp2_end_compress;
+        l_codec->m_codec_data.m_compression.opj_end_compress =
+            opj_jp2_end_compress;
 
-        l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *,
-                struct opj_stream_private *,
-                struct opj_image *,
-                struct opj_event_mgr *))  opj_jp2_start_compress;
+        l_codec->m_codec_data.m_compression.opj_start_compress =
+            opj_jp2_start_compress;
 
-        l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *,
-                OPJ_UINT32,
-                OPJ_BYTE*,
-                OPJ_UINT32,
-                struct opj_stream_private *,
-                struct opj_event_mgr *)) opj_jp2_write_tile;
+        l_codec->m_codec_data.m_compression.opj_write_tile = opj_jp2_write_tile;
 
-        l_codec->m_codec_data.m_compression.opj_destroy = (void (*)(
-                    void *)) opj_jp2_destroy;
+        l_codec->m_codec_data.m_compression.opj_destroy = opj_jp2_destroy;
 
-        l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *,
-                opj_cparameters_t *,
-                struct opj_image *,
-                struct opj_event_mgr *)) opj_jp2_setup_encoder;
+        l_codec->m_codec_data.m_compression.opj_setup_encoder =
+            opj_jp2_setup_encoder;
 
-        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL(
-                    *)(void *,
-                       const char* const*,
-                       struct opj_event_mgr *)) opj_jp2_encoder_set_extra_options;
+        l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options =
+            opj_jp2_encoder_set_extra_options;
 
-        l_codec->opj_set_threads =
-            (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jp2_set_threads;
+        l_codec->opj_set_threads = opj_jp2_set_threads;
 
         l_codec->m_codec = opj_jp2_create(OPJ_FALSE);
         if (! l_codec->m_codec) {
diff --git a/third_party/libtiff/0000-build-config.patch b/third_party/libtiff/0000-build-config.patch
index f66235a..c3925b0 100644
--- a/third_party/libtiff/0000-build-config.patch
+++ b/third_party/libtiff/0000-build-config.patch
@@ -280,3 +280,15 @@
 +#define IPTC_SUPPORT
 +
 +#endif /* _TIFFCONF_ */
+diff --git a/third_party/libtiff/tif_hash_set.c b/third_party/libtiff/tif_hash_set.c
+--- a/third_party/libtiff/tif_hash_set.c
++++ b/third_party/libtiff/tif_hash_set.c
+@@ -26,7 +26,7 @@
+  * DEALINGS IN THE SOFTWARE.
+  ****************************************************************************/
+ 
+-#include "tif_config.h"
++#include "tiffconf.h"
+ 
+ #include "tif_hash_set.h"
+ 
diff --git a/third_party/libtiff/METADATA b/third_party/libtiff/METADATA
new file mode 100644
index 0000000..35e01b9
--- /dev/null
+++ b/third_party/libtiff/METADATA
@@ -0,0 +1,17 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Metadata for CVEs that are fixed or don't affect libtiff.
+
+third_party {
+  security {
+    # PDFium's copy of libtiff does not ship tools like tiffcrop.
+    mitigated_security_patch: "CVE-2022-48281"
+    mitigated_security_patch: "CVE-2023-1916"
+    mitigated_security_patch: "CVE-2023-25433"
+    mitigated_security_patch: "CVE-2023-25434"
+    mitigated_security_patch: "CVE-2023-25435"
+    mitigated_security_patch: "CVE-2023-26965"
+  }
+}
\ No newline at end of file
diff --git a/third_party/libtiff/README.pdfium b/third_party/libtiff/README.pdfium
index a66a692..9eec6dc 100644
--- a/third_party/libtiff/README.pdfium
+++ b/third_party/libtiff/README.pdfium
@@ -1,8 +1,9 @@
 Name: LibTIFF
 URL: http://www.simplesystems.org/libtiff/
-Version: 4.5.0
-CPEPrefix: cpe:/a:libtiff:libtiff:4.5.0
+Version: 4.5.1
+CPEPrefix: cpe:/a:libtiff:libtiff:4.5.1
 Security Critical: yes
+Shipped: yes
 License: BSD
 
 Description:
diff --git a/third_party/libtiff/tif_close.c b/third_party/libtiff/tif_close.c
index 06aa29f..907d7f1 100644
--- a/third_party/libtiff/tif_close.c
+++ b/third_party/libtiff/tif_close.c
@@ -51,8 +51,7 @@
     (*tif->tif_cleanup)(tif);
     TIFFFreeDirectory(tif);
 
-    TIFFHashSetDestroy(tif->tif_map_dir_offset_to_number);
-    TIFFHashSetDestroy(tif->tif_map_dir_number_to_offset);
+    _TIFFCleanupIFDOffsetAndNumberMaps(tif);
 
     /*
      * Clean up client info links.
@@ -115,6 +114,24 @@
 }
 
 /************************************************************************/
+/*                    _TIFFCleanupIFDOffsetAndNumberMaps()              */
+/************************************************************************/
+
+void _TIFFCleanupIFDOffsetAndNumberMaps(TIFF *tif)
+{
+    if (tif->tif_map_dir_offset_to_number)
+    {
+        TIFFHashSetDestroy(tif->tif_map_dir_offset_to_number);
+        tif->tif_map_dir_offset_to_number = NULL;
+    }
+    if (tif->tif_map_dir_number_to_offset)
+    {
+        TIFFHashSetDestroy(tif->tif_map_dir_number_to_offset);
+        tif->tif_map_dir_number_to_offset = NULL;
+    }
+}
+
+/************************************************************************/
 /*                            TIFFClose()                               */
 /************************************************************************/
 
@@ -130,9 +147,12 @@
 
 void TIFFClose(TIFF *tif)
 {
-    TIFFCloseProc closeproc = tif->tif_closeproc;
-    thandle_t fd = tif->tif_clientdata;
+    if (tif != NULL)
+    {
+        TIFFCloseProc closeproc = tif->tif_closeproc;
+        thandle_t fd = tif->tif_clientdata;
 
-    TIFFCleanup(tif);
-    (void)(*closeproc)(fd);
+        TIFFCleanup(tif);
+        (void)(*closeproc)(fd);
+    }
 }
diff --git a/third_party/libtiff/tif_dir.c b/third_party/libtiff/tif_dir.c
index 4a36654..8500621 100644
--- a/third_party/libtiff/tif_dir.c
+++ b/third_party/libtiff/tif_dir.c
@@ -192,11 +192,11 @@
 static uint16_t countInkNamesString(TIFF *tif, uint32_t slen, const char *s)
 {
     uint16_t i = 0;
-    const char *ep = s + slen;
-    const char *cp = s;
 
     if (slen > 0)
     {
+        const char *ep = s + slen;
+        const char *cp = s;
         do
         {
             for (; cp < ep && *cp != '\0'; cp++)
@@ -1621,7 +1621,7 @@
     TIFFDirectory *td = &tif->tif_dir;
     int i;
 
-    _TIFFmemset(td->td_fieldsset, 0, FIELD_SETLONGS);
+    _TIFFmemset(td->td_fieldsset, 0, sizeof(td->td_fieldsset));
     CleanupField(td_sminsamplevalue);
     CleanupField(td_smaxsamplevalue);
     CleanupField(td_colormap[0]);
@@ -1702,6 +1702,12 @@
     tif->tif_curoff = 0;
     tif->tif_row = (uint32_t)-1;
     tif->tif_curstrip = (uint32_t)-1;
+    /* invalidate directory index */
+    tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+    /* invalidate IFD loop lists */
+    _TIFFCleanupIFDOffsetAndNumberMaps(tif);
+    /* To be able to return from SubIFD or custom-IFD to main-IFD */
+    tif->tif_setdirectory_force_absolute = TRUE;
 
     return 0;
 }
@@ -2022,29 +2028,78 @@
 int TIFFSetDirectory(TIFF *tif, tdir_t dirn)
 {
     uint64_t nextdiroff;
-    tdir_t nextdirnum;
+    tdir_t nextdirnum = 0;
     tdir_t n;
 
-    if (!(tif->tif_flags & TIFF_BIGTIFF))
-        nextdiroff = tif->tif_header.classic.tiff_diroff;
-    else
-        nextdiroff = tif->tif_header.big.tiff_diroff;
-    nextdirnum = 0;
-    for (n = dirn; n > 0 && nextdiroff != 0; n--)
-        if (!TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum))
-            return (0);
-    /* If the n-th directory could not be reached (does not exist),
-     * return here without touching anything further. */
-    if (nextdiroff == 0 || n > 0)
-        return (0);
+    if (tif->tif_setdirectory_force_absolute)
+    {
+        /* tif_setdirectory_force_absolute=1 will force parsing the main IFD
+         * chain from the beginning, thus IFD directory list needs to be cleared
+         * from possible SubIFD offsets.
+         */
+        _TIFFCleanupIFDOffsetAndNumberMaps(tif); /* invalidate IFD loop lists */
+    }
 
-    tif->tif_nextdiroff = nextdiroff;
-    /*
-     * Set curdir to the actual directory index.  The
-     * -1 is because TIFFReadDirectory will increment
-     * tif_curdir after successfully reading the directory.
-     */
-    tif->tif_curdir = (dirn - n) - 1;
+    /* Even faster path, if offset is available within IFD loop hash list. */
+    if (!tif->tif_setdirectory_force_absolute &&
+        _TIFFGetOffsetFromDirNumber(tif, dirn, &nextdiroff))
+    {
+        /* Set parameters for following TIFFReadDirectory() below. */
+        tif->tif_nextdiroff = nextdiroff;
+        tif->tif_curdir = dirn;
+        /* Reset to relative stepping */
+        tif->tif_setdirectory_force_absolute = FALSE;
+    }
+    else
+    {
+
+        /* Fast path when we just advance relative to the current directory:
+         * start at the current dir offset and continue to seek from there.
+         * Check special cases when relative is not allowed:
+         * - jump back from SubIFD or custom directory
+         * - right after TIFFWriteDirectory() jump back to that directory
+         *   using TIFFSetDirectory() */
+        const int relative = (dirn >= tif->tif_curdir) &&
+                             (tif->tif_diroff != 0) &&
+                             !tif->tif_setdirectory_force_absolute;
+
+        if (relative)
+        {
+            nextdiroff = tif->tif_diroff;
+            dirn -= tif->tif_curdir;
+            nextdirnum = tif->tif_curdir;
+        }
+        else if (!(tif->tif_flags & TIFF_BIGTIFF))
+            nextdiroff = tif->tif_header.classic.tiff_diroff;
+        else
+            nextdiroff = tif->tif_header.big.tiff_diroff;
+
+        /* Reset to relative stepping */
+        tif->tif_setdirectory_force_absolute = FALSE;
+
+        for (n = dirn; n > 0 && nextdiroff != 0; n--)
+            if (!TIFFAdvanceDirectory(tif, &nextdiroff, NULL, &nextdirnum))
+                return (0);
+        /* If the n-th directory could not be reached (does not exist),
+         * return here without touching anything further. */
+        if (nextdiroff == 0 || n > 0)
+            return (0);
+
+        tif->tif_nextdiroff = nextdiroff;
+
+        /* Set curdir to the actual directory index. */
+        if (relative)
+            tif->tif_curdir += dirn - n;
+        else
+            tif->tif_curdir = dirn - n;
+    }
+
+    /* The -1 decrement is because TIFFReadDirectory will increment
+     * tif_curdir after successfully reading the directory. */
+    if (tif->tif_curdir == 0)
+        tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+    else
+        tif->tif_curdir--;
     return (TIFFReadDirectory(tif));
 }
 
@@ -2088,8 +2143,8 @@
     tif->tif_nextdiroff = diroff;
     retval = TIFFReadDirectory(tif);
     /* If failed, curdir was not incremented in TIFFReadDirectory(), so set it
-     * back. */
-    if (!retval)
+     * back, but leave it for diroff==0. */
+    if (!retval && diroff != 0)
     {
         if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
             tif->tif_curdir = 0;
@@ -2100,10 +2155,12 @@
     {
         /* Reset IFD list to start new one for SubIFD chain and also start
          * SubIFD chain with tif_curdir=0. */
-        tif->tif_dirnumber = 0;
+        _TIFFCleanupIFDOffsetAndNumberMaps(tif); /* invalidate IFD loop lists */
         tif->tif_curdir = 0; /* first directory of new chain */
         /* add this offset to new IFD list */
         _TIFFCheckDirNumberAndOffset(tif, tif->tif_curdir, diroff);
+        /* To be able to return from SubIFD or custom-IFD to main-IFD */
+        tif->tif_setdirectory_force_absolute = TRUE;
     }
     return (retval);
 }
@@ -2139,6 +2196,13 @@
                       "Can not unlink directory in read-only file");
         return (0);
     }
+    if (dirn == 0)
+    {
+        TIFFErrorExtR(tif, module,
+                      "For TIFFUnlinkDirectory() first directory starts with "
+                      "number 1 and not 0");
+        return (0);
+    }
     /*
      * Go to the directory before the one we want
      * to unlink and nab the offset of the link
@@ -2201,6 +2265,17 @@
             return (0);
         }
     }
+
+    /* For dirn=1 (first directory) also update the libtiff internal
+     * base offset variables. */
+    if (dirn == 1)
+    {
+        if (!(tif->tif_flags & TIFF_BIGTIFF))
+            tif->tif_header.classic.tiff_diroff = (uint32_t)nextdir;
+        else
+            tif->tif_header.big.tiff_diroff = nextdir;
+    }
+
     /*
      * Leave directory state setup safely.  We don't have
      * facilities for doing inserting and removing directories,
@@ -2227,5 +2302,7 @@
     tif->tif_curoff = 0;
     tif->tif_row = (uint32_t)-1;
     tif->tif_curstrip = (uint32_t)-1;
+    tif->tif_curdir = TIFF_NON_EXISTENT_DIR_NUMBER;
+    _TIFFCleanupIFDOffsetAndNumberMaps(tif); /* invalidate IFD loop lists */
     return (1);
 }
diff --git a/third_party/libtiff/tif_dir.h b/third_party/libtiff/tif_dir.h
index fad1eb0..9eaf22f 100644
--- a/third_party/libtiff/tif_dir.h
+++ b/third_party/libtiff/tif_dir.h
@@ -70,9 +70,9 @@
  */
 typedef struct
 {
-#define FIELD_SETLONGS 4
+#define FIELDSET_ITEMS 4
     /* bit vector of fields that are set */
-    unsigned long td_fieldsset[FIELD_SETLONGS];
+    uint32_t td_fieldsset[FIELDSET_ITEMS];
 
     uint32_t td_imagewidth, td_imagelength, td_imagedepth;
     uint32_t td_tilewidth, td_tilelength, td_tiledepth;
@@ -202,9 +202,9 @@
  */
 #define FIELD_PSEUDO 0
 
-#define FIELD_LAST (32 * FIELD_SETLONGS - 1)
+#define FIELD_LAST (32 * FIELDSET_ITEMS - 1)
 
-#define BITn(n) (((unsigned long)1L) << ((n)&0x1f))
+#define BITn(n) (((uint32_t)1L) << ((n)&0x1f))
 #define BITFIELDn(tif, n) ((tif)->tif_dir.td_fieldsset[(n) / 32])
 #define TIFFFieldSet(tif, field) (BITFIELDn(tif, field) & BITn(field))
 #define TIFFSetFieldBit(tif, field) (BITFIELDn(tif, field) |= BITn(field))
@@ -329,6 +329,10 @@
                                             uint64_t diroff);
     extern int _TIFFGetDirNumberFromOffset(TIFF *tif, uint64_t diroff,
                                            tdir_t *dirn);
+    extern int _TIFFGetOffsetFromDirNumber(TIFF *tif, tdir_t dirn,
+                                           uint64_t *diroff);
+    extern int _TIFFRemoveEntryFromDirectoryListByOffset(TIFF *tif,
+                                                         uint64_t diroff);
 
 #if defined(__cplusplus)
 }
diff --git a/third_party/libtiff/tif_dirinfo.c b/third_party/libtiff/tif_dirinfo.c
index 6a15c76..0e705e8 100644
--- a/third_party/libtiff/tif_dirinfo.c
+++ b/third_party/libtiff/tif_dirinfo.c
@@ -63,351 +63,283 @@
  *
  */
 
+/* clang-format off */ /* for better readability of tag comments */
 static const TIFFField tiffFields[] = {
-    {TIFFTAG_SUBFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_SUBFILETYPE, 1, 0, "SubfileType", NULL},
-    {TIFFTAG_OSUBFILETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "OldSubfileType", NULL},
-    {TIFFTAG_IMAGEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 0, 0, "ImageWidth", NULL},
-    {TIFFTAG_IMAGELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 1, 0, "ImageLength", NULL},
-    {TIFFTAG_BITSPERSAMPLE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_BITSPERSAMPLE, 0, 0, "BitsPerSample", NULL},
-    {TIFFTAG_COMPRESSION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_COMPRESSION, 0, 0, "Compression", NULL},
-    {TIFFTAG_PHOTOMETRIC, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_PHOTOMETRIC, 0, 0,
-     "PhotometricInterpretation", NULL},
-    {TIFFTAG_THRESHHOLDING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_THRESHHOLDING, 1, 0, "Threshholding", NULL},
-    {TIFFTAG_CELLWIDTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CellWidth", NULL},
-    {TIFFTAG_CELLLENGTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CellLength", NULL},
-    {TIFFTAG_FILLORDER, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_FILLORDER, 0, 0, "FillOrder", NULL},
-    {TIFFTAG_DOCUMENTNAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DocumentName", NULL},
-    {TIFFTAG_IMAGEDESCRIPTION, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageDescription", NULL},
-    {TIFFTAG_MAKE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Make", NULL},
-    {TIFFTAG_MODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Model", NULL},
-    {TIFFTAG_STRIPOFFSETS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_STRIPOFFSETS, 0, 0, "StripOffsets", NULL},
-    {TIFFTAG_ORIENTATION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_ORIENTATION, 0, 0, "Orientation", NULL},
-    {TIFFTAG_SAMPLESPERPIXEL, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_SAMPLESPERPIXEL, 0, 0, "SamplesPerPixel",
-     NULL},
-    {TIFFTAG_ROWSPERSTRIP, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_ROWSPERSTRIP, 0, 0, "RowsPerStrip", NULL},
-    {TIFFTAG_STRIPBYTECOUNTS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_STRIPBYTECOUNTS, 0, 0, "StripByteCounts",
-     NULL},
-    {TIFFTAG_MINSAMPLEVALUE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_MINSAMPLEVALUE, 1, 0, "MinSampleValue", NULL},
-    {TIFFTAG_MAXSAMPLEVALUE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_MAXSAMPLEVALUE, 1, 0, "MaxSampleValue", NULL},
-    {TIFFTAG_XRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "XResolution", NULL},
-    {TIFFTAG_YRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "YResolution", NULL},
-    {TIFFTAG_PLANARCONFIG, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_PLANARCONFIG, 0, 0, "PlanarConfiguration",
-     NULL},
-    {TIFFTAG_PAGENAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PageName", NULL},
-    {TIFFTAG_XPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "XPosition", NULL},
-    {TIFFTAG_YPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "YPosition", NULL},
-    {TIFFTAG_FREEOFFSETS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 0, 0, "FreeOffsets", NULL},
-    {TIFFTAG_FREEBYTECOUNTS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 0, 0, "FreeByteCounts", NULL},
-    {TIFFTAG_GRAYRESPONSEUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "GrayResponseUnit", NULL},
-    {TIFFTAG_GRAYRESPONSECURVE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "GrayResponseCurve", NULL},
-    {TIFFTAG_RESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_RESOLUTIONUNIT, 1, 0, "ResolutionUnit", NULL},
-    {TIFFTAG_PAGENUMBER, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR,
-     TIFF_SETGET_UNDEFINED, FIELD_PAGENUMBER, 1, 0, "PageNumber", NULL},
-    {TIFFTAG_COLORRESPONSEUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "ColorResponseUnit", NULL},
-    {TIFFTAG_TRANSFERFUNCTION, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_OTHER,
-     TIFF_SETGET_UNDEFINED, FIELD_TRANSFERFUNCTION, 1, 0, "TransferFunction",
-     NULL},
-    {TIFFTAG_SOFTWARE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Software", NULL},
-    {TIFFTAG_DATETIME, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTime", NULL},
-    {TIFFTAG_ARTIST, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Artist", NULL},
-    {TIFFTAG_HOSTCOMPUTER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "HostComputer", NULL},
-    {TIFFTAG_WHITEPOINT, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WhitePoint", NULL},
-    {TIFFTAG_PRIMARYCHROMATICITIES, 6, 6, TIFF_RATIONAL, 0,
-     TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0,
-     "PrimaryChromaticities", NULL},
-    {TIFFTAG_COLORMAP, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_OTHER,
-     TIFF_SETGET_UNDEFINED, FIELD_COLORMAP, 1, 0, "ColorMap", NULL},
-    {TIFFTAG_HALFTONEHINTS, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR,
-     TIFF_SETGET_UNDEFINED, FIELD_HALFTONEHINTS, 1, 0, "HalftoneHints", NULL},
-    {TIFFTAG_TILEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_TILEDIMENSIONS, 0, 0, "TileWidth", NULL},
-    {TIFFTAG_TILELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_TILEDIMENSIONS, 0, 0, "TileLength", NULL},
-    {TIFFTAG_TILEOFFSETS, -1, 1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_STRIPOFFSETS, 0, 0, "TileOffsets", NULL},
-    {TIFFTAG_TILEBYTECOUNTS, -1, 1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_STRIPBYTECOUNTS, 0, 0, "TileByteCounts",
-     NULL},
-    {TIFFTAG_SUBIFD, -1, -1, TIFF_IFD8, 0, TIFF_SETGET_C16_IFD8,
-     TIFF_SETGET_UNDEFINED, FIELD_SUBIFD, 1, 1, "SubIFD",
-     (TIFFFieldArray *)&tiffFieldArray},
-    {TIFFTAG_INKSET, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "InkSet", NULL},
-    {TIFFTAG_INKNAMES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_C16_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_INKNAMES, 1, 1, "InkNames", NULL},
-    {TIFFTAG_NUMBEROFINKS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_NUMBEROFINKS, 1, 0, "NumberOfInks", NULL},
-    {TIFFTAG_DOTRANGE, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DotRange", NULL},
-    {TIFFTAG_TARGETPRINTER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TargetPrinter", NULL},
-    {TIFFTAG_EXTRASAMPLES, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_EXTRASAMPLES, 0, 1, "ExtraSamples", NULL},
-    {TIFFTAG_SAMPLEFORMAT, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_SAMPLEFORMAT, 0, 0, "SampleFormat", NULL},
-    {TIFFTAG_SMINSAMPLEVALUE, -2, -1, TIFF_ANY, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_SMINSAMPLEVALUE, 1, 0, "SMinSampleValue",
-     NULL},
-    {TIFFTAG_SMAXSAMPLEVALUE, -2, -1, TIFF_ANY, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_SMAXSAMPLEVALUE, 1, 0, "SMaxSampleValue",
-     NULL},
-    {TIFFTAG_CLIPPATH, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ClipPath", NULL},
-    {TIFFTAG_XCLIPPATHUNITS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "XClipPathUnits", NULL},
-    {TIFFTAG_YCLIPPATHUNITS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "YClipPathUnits", NULL},
-    {TIFFTAG_YCBCRCOEFFICIENTS, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "YCbCrCoefficients", NULL},
-    {TIFFTAG_YCBCRSUBSAMPLING, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR,
-     TIFF_SETGET_UNDEFINED, FIELD_YCBCRSUBSAMPLING, 0, 0, "YCbCrSubsampling",
-     NULL},
-    {TIFFTAG_YCBCRPOSITIONING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_YCBCRPOSITIONING, 0, 0, "YCbCrPositioning",
-     NULL},
-    {TIFFTAG_REFERENCEBLACKWHITE, 6, 6, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_REFBLACKWHITE, 1, 0, "ReferenceBlackWhite",
-     NULL},
-    {TIFFTAG_XMLPACKET, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "XMLPacket", NULL},
+    {TIFFTAG_SUBFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_SUBFILETYPE, 1, 0, "SubfileType", NULL},
+    {TIFFTAG_OSUBFILETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "OldSubfileType", NULL},
+    {TIFFTAG_IMAGEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 0, 0, "ImageWidth", NULL},
+    {TIFFTAG_IMAGELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 1, 0, "ImageLength", NULL},
+    {TIFFTAG_BITSPERSAMPLE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_BITSPERSAMPLE, 0, 0, "BitsPerSample", NULL},
+    {TIFFTAG_COMPRESSION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_COMPRESSION, 0, 0, "Compression", NULL},
+    {TIFFTAG_PHOTOMETRIC, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_PHOTOMETRIC, 0, 0, "PhotometricInterpretation", NULL},
+    {TIFFTAG_THRESHHOLDING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_THRESHHOLDING, 1, 0, "Threshholding", NULL},
+    {TIFFTAG_CELLWIDTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CellWidth", NULL},
+    {TIFFTAG_CELLLENGTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CellLength", NULL},
+    {TIFFTAG_FILLORDER, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_FILLORDER, 0, 0, "FillOrder", NULL},
+    {TIFFTAG_DOCUMENTNAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DocumentName", NULL},
+    {TIFFTAG_IMAGEDESCRIPTION, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageDescription", NULL},
+    {TIFFTAG_MAKE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Make", NULL},
+    {TIFFTAG_MODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Model", NULL},
+    {TIFFTAG_STRIPOFFSETS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPOFFSETS, 0, 0, "StripOffsets", NULL},
+    {TIFFTAG_ORIENTATION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_ORIENTATION, 0, 0, "Orientation", NULL},
+    {TIFFTAG_SAMPLESPERPIXEL, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_SAMPLESPERPIXEL, 0, 0, "SamplesPerPixel", NULL},
+    {TIFFTAG_ROWSPERSTRIP, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_ROWSPERSTRIP, 0, 0, "RowsPerStrip", NULL},
+    {TIFFTAG_STRIPBYTECOUNTS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPBYTECOUNTS, 0, 0, "StripByteCounts", NULL},
+    {TIFFTAG_MINSAMPLEVALUE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_MINSAMPLEVALUE, 1, 0, "MinSampleValue", NULL},
+    {TIFFTAG_MAXSAMPLEVALUE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_MAXSAMPLEVALUE, 1, 0, "MaxSampleValue", NULL},
+    {TIFFTAG_XRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "XResolution", NULL},
+    {TIFFTAG_YRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "YResolution", NULL},
+    {TIFFTAG_PLANARCONFIG, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_PLANARCONFIG, 0, 0, "PlanarConfiguration", NULL},
+    {TIFFTAG_PAGENAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PageName", NULL},
+    {TIFFTAG_XPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "XPosition", NULL},
+    {TIFFTAG_YPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "YPosition", NULL},
+    {TIFFTAG_FREEOFFSETS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 0, 0, "FreeOffsets", NULL},
+    {TIFFTAG_FREEBYTECOUNTS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 0, 0, "FreeByteCounts", NULL},
+    {TIFFTAG_GRAYRESPONSEUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "GrayResponseUnit", NULL},
+    {TIFFTAG_GRAYRESPONSECURVE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "GrayResponseCurve", NULL},
+    {TIFFTAG_RESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTIONUNIT, 1, 0, "ResolutionUnit", NULL},
+    {TIFFTAG_PAGENUMBER, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_PAGENUMBER, 1, 0, "PageNumber", NULL},
+    {TIFFTAG_COLORRESPONSEUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "ColorResponseUnit", NULL},
+    {TIFFTAG_TRANSFERFUNCTION, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_OTHER, TIFF_SETGET_UNDEFINED, FIELD_TRANSFERFUNCTION, 1, 0, "TransferFunction", NULL},
+    {TIFFTAG_SOFTWARE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Software", NULL},
+    {TIFFTAG_DATETIME, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTime", NULL},
+    {TIFFTAG_ARTIST, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Artist", NULL},
+    {TIFFTAG_HOSTCOMPUTER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "HostComputer", NULL},
+    {TIFFTAG_WHITEPOINT, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WhitePoint", NULL},
+    {TIFFTAG_PRIMARYCHROMATICITIES, 6, 6, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PrimaryChromaticities", NULL},
+    {TIFFTAG_COLORMAP, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_OTHER, TIFF_SETGET_UNDEFINED, FIELD_COLORMAP, 1, 0, "ColorMap", NULL},
+    {TIFFTAG_HALFTONEHINTS, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_HALFTONEHINTS, 1, 0, "HalftoneHints", NULL},
+    {TIFFTAG_TILEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_TILEDIMENSIONS, 0, 0, "TileWidth", NULL},
+    {TIFFTAG_TILELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_TILEDIMENSIONS, 0, 0, "TileLength", NULL},
+    {TIFFTAG_TILEOFFSETS, -1, 1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPOFFSETS, 0, 0, "TileOffsets", NULL},
+    {TIFFTAG_TILEBYTECOUNTS, -1, 1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPBYTECOUNTS, 0, 0, "TileByteCounts", NULL},
+    {TIFFTAG_SUBIFD, -1, -1, TIFF_IFD8, 0, TIFF_SETGET_C16_IFD8, TIFF_SETGET_UNDEFINED, FIELD_SUBIFD, 1, 1, "SubIFD", (TIFFFieldArray *)&tiffFieldArray},
+    {TIFFTAG_INKSET, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "InkSet", NULL},
+    {TIFFTAG_INKNAMES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_C16_ASCII, TIFF_SETGET_UNDEFINED, FIELD_INKNAMES, 1, 1, "InkNames", NULL},
+    {TIFFTAG_NUMBEROFINKS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_NUMBEROFINKS, 1, 0, "NumberOfInks", NULL},
+    {TIFFTAG_DOTRANGE, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DotRange", NULL},
+    {TIFFTAG_TARGETPRINTER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TargetPrinter", NULL},
+    {TIFFTAG_EXTRASAMPLES, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_EXTRASAMPLES, 0, 1, "ExtraSamples", NULL},
+    {TIFFTAG_SAMPLEFORMAT, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_SAMPLEFORMAT, 0, 0, "SampleFormat", NULL},
+    {TIFFTAG_SMINSAMPLEVALUE, -2, -1, TIFF_ANY, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_SMINSAMPLEVALUE, 1, 0, "SMinSampleValue", NULL},
+    {TIFFTAG_SMAXSAMPLEVALUE, -2, -1, TIFF_ANY, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_SMAXSAMPLEVALUE, 1, 0, "SMaxSampleValue", NULL},
+    {TIFFTAG_CLIPPATH, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ClipPath", NULL},
+    {TIFFTAG_XCLIPPATHUNITS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "XClipPathUnits", NULL},
+    {TIFFTAG_YCLIPPATHUNITS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "YClipPathUnits", NULL},
+    {TIFFTAG_YCBCRCOEFFICIENTS, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "YCbCrCoefficients", NULL},
+    {TIFFTAG_YCBCRSUBSAMPLING, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_YCBCRSUBSAMPLING, 0, 0, "YCbCrSubsampling", NULL},
+    {TIFFTAG_YCBCRPOSITIONING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_YCBCRPOSITIONING, 0, 0, "YCbCrPositioning", NULL},
+    {TIFFTAG_REFERENCEBLACKWHITE, 6, 6, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_REFBLACKWHITE, 1, 0, "ReferenceBlackWhite", NULL},
+    {TIFFTAG_XMLPACKET, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "XMLPacket", NULL},
     /* begin SGI tags */
-    {TIFFTAG_MATTEING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_EXTRASAMPLES, 0, 0, "Matteing", NULL},
-    {TIFFTAG_DATATYPE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_SAMPLEFORMAT, 0, 0, "DataType", NULL},
-    {TIFFTAG_IMAGEDEPTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_IMAGEDEPTH, 0, 0, "ImageDepth", NULL},
-    {TIFFTAG_TILEDEPTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_TILEDEPTH, 0, 0, "TileDepth", NULL},
+    {TIFFTAG_MATTEING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_EXTRASAMPLES, 0, 0, "Matteing", NULL},
+    {TIFFTAG_DATATYPE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_SAMPLEFORMAT, 0, 0, "DataType", NULL},
+    {TIFFTAG_IMAGEDEPTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDEPTH, 0, 0, "ImageDepth", NULL},
+    {TIFFTAG_TILEDEPTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_TILEDEPTH, 0, 0, "TileDepth", NULL},
     /* end SGI tags */
     /* begin Pixar tags */
-    {TIFFTAG_PIXAR_IMAGEFULLWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageFullWidth", NULL},
-    {TIFFTAG_PIXAR_IMAGEFULLLENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageFullLength", NULL},
-    {TIFFTAG_PIXAR_TEXTUREFORMAT, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TextureFormat", NULL},
-    {TIFFTAG_PIXAR_WRAPMODES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TextureWrapModes", NULL},
-    {TIFFTAG_PIXAR_FOVCOT, 1, 1, TIFF_FLOAT, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FieldOfViewCotangent", NULL},
-    {TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, 16, 16, TIFF_FLOAT, 0,
-     TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0,
-     "MatrixWorldToScreen", NULL},
-    {TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, 16, 16, TIFF_FLOAT, 0,
-     TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0,
-     "MatrixWorldToCamera", NULL},
-    {TIFFTAG_CFAREPEATPATTERNDIM, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CFARepeatPatternDim", NULL},
-    {TIFFTAG_CFAPATTERN, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CFAPattern", NULL},
-    {TIFFTAG_COPYRIGHT, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Copyright", NULL},
+    {TIFFTAG_PIXAR_IMAGEFULLWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageFullWidth", NULL},
+    {TIFFTAG_PIXAR_IMAGEFULLLENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageFullLength", NULL},
+    {TIFFTAG_PIXAR_TEXTUREFORMAT, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TextureFormat", NULL},
+    {TIFFTAG_PIXAR_WRAPMODES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TextureWrapModes", NULL},
+    {TIFFTAG_PIXAR_FOVCOT, 1, 1, TIFF_FLOAT, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FieldOfViewCotangent", NULL},
+    {TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, 16, 16, TIFF_FLOAT, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MatrixWorldToScreen", NULL},
+    {TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, 16, 16, TIFF_FLOAT, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MatrixWorldToCamera", NULL},
+    {TIFFTAG_COPYRIGHT, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Copyright", NULL},
     /* end Pixar tags */
-    {TIFFTAG_RICHTIFFIPTC, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "RichTIFFIPTC", NULL},
-    {TIFFTAG_PHOTOSHOP, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "Photoshop", NULL},
-    /*--: EXIFIFD and GPSIFD specified as TIFF_LONG by Aware-Systems and not
-     * TIFF_IFD8 as in original LibTiff. However, for IFD-like tags, libtiff
-     * uses the data type TIFF_IFD8 in tiffFields[]-tag definition combined with
-     *    a special handling procedure in order to write either a 32-bit value
-     * and the TIFF_IFD type-id into ClassicTIFF files or a 64-bit value and the
-     * TIFF_IFD8 type-id into BigTIFF files. */
-    {TIFFTAG_EXIFIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EXIFIFDOffset",
-     (TIFFFieldArray *)&exifFieldArray},
-    {TIFFTAG_ICCPROFILE, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ICC Profile", NULL},
-    {TIFFTAG_GPSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSIFDOffset",
-     (TIFFFieldArray *)&gpsFieldArray},
-    {TIFFTAG_FAXRECVPARAMS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UINT32, FIELD_CUSTOM, TRUE, FALSE, "FaxRecvParams", NULL},
-    {TIFFTAG_FAXSUBADDRESS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_ASCII, FIELD_CUSTOM, TRUE, FALSE, "FaxSubAddress", NULL},
-    {TIFFTAG_FAXRECVTIME, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UINT32, FIELD_CUSTOM, TRUE, FALSE, "FaxRecvTime", NULL},
-    {TIFFTAG_FAXDCS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_ASCII, FIELD_CUSTOM, TRUE, FALSE, "FaxDcs", NULL},
-    {TIFFTAG_STONITS, 1, 1, TIFF_DOUBLE, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "StoNits", NULL},
-    {TIFFTAG_IMAGESOURCEDATA, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1,
-     "Adobe Photoshop Document Data Block", NULL},
-    {TIFFTAG_INTEROPERABILITYIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "InteroperabilityIFDOffset",
-     NULL},
+    {TIFFTAG_RICHTIFFIPTC, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "RichTIFFIPTC", NULL},
+    {TIFFTAG_PHOTOSHOP, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "Photoshop", NULL},
+    /*--: EXIFIFD and GPSIFD specified as TIFF_LONG by Aware-Systems and not TIFF_IFD8 as in original LibTiff. However, for IFD-like tags,
+     * libtiff uses the data type TIFF_IFD8 in tiffFields[]-tag definition combined with a special handling procedure in order to write either
+     * a 32-bit value and the TIFF_IFD type-id into ClassicTIFF files or a 64-bit value and the TIFF_IFD8 type-id into BigTIFF files. */
+    {TIFFTAG_EXIFIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EXIFIFDOffset", (TIFFFieldArray *)&exifFieldArray},
+    {TIFFTAG_ICCPROFILE, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ICC Profile", NULL},
+    {TIFFTAG_GPSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSIFDOffset", (TIFFFieldArray *)&gpsFieldArray},
+    {TIFFTAG_FAXRECVPARAMS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_CUSTOM, TRUE, FALSE, "FaxRecvParams", NULL},
+    {TIFFTAG_FAXSUBADDRESS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, TRUE, FALSE, "FaxSubAddress", NULL},
+    {TIFFTAG_FAXRECVTIME, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_CUSTOM, TRUE, FALSE, "FaxRecvTime", NULL},
+    {TIFFTAG_FAXDCS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, TRUE, FALSE, "FaxDcs", NULL},
+    {TIFFTAG_STONITS, 1, 1, TIFF_DOUBLE, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "StoNits", NULL},
+    {TIFFTAG_IMAGESOURCEDATA, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "Adobe Photoshop Document Data Block", NULL},
+    {TIFFTAG_INTEROPERABILITYIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "InteroperabilityIFDOffset", NULL},
     /* begin DNG tags */
-    {TIFFTAG_DNGVERSION, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DNGVersion", NULL},
-    {TIFFTAG_DNGBACKWARDVERSION, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DNGBackwardVersion", NULL},
-    {TIFFTAG_UNIQUECAMERAMODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "UniqueCameraModel", NULL},
-    {TIFFTAG_LOCALIZEDCAMERAMODEL, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "LocalizedCameraModel", NULL},
-    {TIFFTAG_CFAPLANECOLOR, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CFAPlaneColor", NULL},
-    {TIFFTAG_CFALAYOUT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CFALayout", NULL},
-    {TIFFTAG_LINEARIZATIONTABLE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "LinearizationTable", NULL},
-    {TIFFTAG_BLACKLEVELREPEATDIM, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BlackLevelRepeatDim", NULL},
-    {TIFFTAG_BLACKLEVEL, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "BlackLevel", NULL},
-    {TIFFTAG_BLACKLEVELDELTAH, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "BlackLevelDeltaH", NULL},
-    {TIFFTAG_BLACKLEVELDELTAV, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "BlackLevelDeltaV", NULL},
-    {TIFFTAG_WHITELEVEL, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "WhiteLevel", NULL},
-    {TIFFTAG_DEFAULTSCALE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DefaultScale", NULL},
-    {TIFFTAG_BESTQUALITYSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BestQualityScale", NULL},
-    {TIFFTAG_DEFAULTCROPORIGIN, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DefaultCropOrigin", NULL},
-    {TIFFTAG_DEFAULTCROPSIZE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DefaultCropSize", NULL},
-    {TIFFTAG_COLORMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ColorMatrix1", NULL},
-    {TIFFTAG_COLORMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ColorMatrix2", NULL},
-    {TIFFTAG_CAMERACALIBRATION1, -1, -1, TIFF_SRATIONAL, 0,
-     TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1,
-     "CameraCalibration1", NULL},
-    {TIFFTAG_CAMERACALIBRATION2, -1, -1, TIFF_SRATIONAL, 0,
-     TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1,
-     "CameraCalibration2", NULL},
-    {TIFFTAG_REDUCTIONMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ReductionMatrix1", NULL},
-    {TIFFTAG_REDUCTIONMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ReductionMatrix2", NULL},
-    {TIFFTAG_ANALOGBALANCE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AnalogBalance", NULL},
-    {TIFFTAG_ASSHOTNEUTRAL, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AsShotNeutral", NULL},
-    {TIFFTAG_ASSHOTWHITEXY, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "AsShotWhiteXY", NULL},
-    {TIFFTAG_BASELINEEXPOSURE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineExposure", NULL},
-    {TIFFTAG_BASELINENOISE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineNoise", NULL},
-    {TIFFTAG_BASELINESHARPNESS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineSharpness", NULL},
-    {TIFFTAG_BAYERGREENSPLIT, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BayerGreenSplit", NULL},
-    {TIFFTAG_LINEARRESPONSELIMIT, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "LinearResponseLimit", NULL},
-    {TIFFTAG_CAMERASERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraSerialNumber", NULL},
-    {TIFFTAG_LENSINFO, 4, 4, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "LensInfo", NULL},
-    {TIFFTAG_CHROMABLURRADIUS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ChromaBlurRadius", NULL},
-    {TIFFTAG_ANTIALIASSTRENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "AntiAliasStrength", NULL},
-    {TIFFTAG_SHADOWSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ShadowScale", NULL},
-    {TIFFTAG_DNGPRIVATEDATA, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "DNGPrivateData", NULL},
-    {TIFFTAG_MAKERNOTESAFETY, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "MakerNoteSafety", NULL},
-    {TIFFTAG_CALIBRATIONILLUMINANT1, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CalibrationIlluminant1", NULL},
-    {TIFFTAG_CALIBRATIONILLUMINANT2, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CalibrationIlluminant2", NULL},
-    {TIFFTAG_RAWDATAUNIQUEID, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "RawDataUniqueID", NULL},
-    {TIFFTAG_ORIGINALRAWFILENAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OriginalRawFileName", NULL},
-    {TIFFTAG_ORIGINALRAWFILEDATA, -1, -1, TIFF_UNDEFINED, 0,
-     TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1,
-     "OriginalRawFileData", NULL},
-    {TIFFTAG_ACTIVEAREA, 4, 4, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ActiveArea", NULL},
-    {TIFFTAG_MASKEDAREAS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "MaskedAreas", NULL},
-    {TIFFTAG_ASSHOTICCPROFILE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AsShotICCProfile", NULL},
-    {TIFFTAG_ASSHOTPREPROFILEMATRIX, -1, -1, TIFF_SRATIONAL, 0,
-     TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1,
-     "AsShotPreProfileMatrix", NULL},
-    {TIFFTAG_CURRENTICCPROFILE, -1, -1, TIFF_UNDEFINED, 0,
-     TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1,
-     "CurrentICCProfile", NULL},
-    {TIFFTAG_CURRENTPREPROFILEMATRIX, -1, -1, TIFF_SRATIONAL, 0,
-     TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1,
-     "CurrentPreProfileMatrix", NULL},
-    {TIFFTAG_PERSAMPLE, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED,
-     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "PerSample", NULL},
+    {TIFFTAG_DNGVERSION, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DNGVersion", NULL},
+    {TIFFTAG_DNGBACKWARDVERSION, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DNGBackwardVersion", NULL},
+    {TIFFTAG_UNIQUECAMERAMODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "UniqueCameraModel", NULL},
+    {TIFFTAG_LOCALIZEDCAMERAMODEL, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "LocalizedCameraModel", NULL},
+    {TIFFTAG_CFAPLANECOLOR, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CFAPlaneColor", NULL},
+    {TIFFTAG_CFALAYOUT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CFALayout", NULL},
+    {TIFFTAG_LINEARIZATIONTABLE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "LinearizationTable", NULL},
+    {TIFFTAG_BLACKLEVELREPEATDIM, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BlackLevelRepeatDim", NULL},
+    {TIFFTAG_BLACKLEVEL, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "BlackLevel", NULL},
+    {TIFFTAG_BLACKLEVELDELTAH, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "BlackLevelDeltaH", NULL},
+    {TIFFTAG_BLACKLEVELDELTAV, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "BlackLevelDeltaV", NULL},
+    {TIFFTAG_WHITELEVEL, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "WhiteLevel", NULL},
+    {TIFFTAG_DEFAULTSCALE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DefaultScale", NULL},
+    {TIFFTAG_BESTQUALITYSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BestQualityScale", NULL},
+    {TIFFTAG_DEFAULTCROPORIGIN, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DefaultCropOrigin", NULL},
+    {TIFFTAG_DEFAULTCROPSIZE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DefaultCropSize", NULL},
+    {TIFFTAG_COLORMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ColorMatrix1", NULL},
+    {TIFFTAG_COLORMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ColorMatrix2", NULL},
+    {TIFFTAG_CAMERACALIBRATION1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CameraCalibration1", NULL},
+    {TIFFTAG_CAMERACALIBRATION2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CameraCalibration2", NULL},
+    {TIFFTAG_REDUCTIONMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ReductionMatrix1", NULL},
+    {TIFFTAG_REDUCTIONMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ReductionMatrix2", NULL},
+    {TIFFTAG_ANALOGBALANCE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AnalogBalance", NULL},
+    {TIFFTAG_ASSHOTNEUTRAL, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AsShotNeutral", NULL},
+    {TIFFTAG_ASSHOTWHITEXY, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "AsShotWhiteXY", NULL},
+    {TIFFTAG_BASELINEEXPOSURE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BaselineExposure", NULL},
+    {TIFFTAG_BASELINENOISE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BaselineNoise", NULL},
+    {TIFFTAG_BASELINESHARPNESS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BaselineSharpness", NULL},
+    {TIFFTAG_BAYERGREENSPLIT, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BayerGreenSplit", NULL},
+    {TIFFTAG_LINEARRESPONSELIMIT, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LinearResponseLimit", NULL},
+    {TIFFTAG_CAMERASERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraSerialNumber", NULL},
+    {TIFFTAG_LENSINFO, 4, 4, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensInfo", NULL},
+    {TIFFTAG_CHROMABLURRADIUS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ChromaBlurRadius", NULL},
+    {TIFFTAG_ANTIALIASSTRENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "AntiAliasStrength", NULL},
+    {TIFFTAG_SHADOWSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ShadowScale", NULL},
+    {TIFFTAG_DNGPRIVATEDATA, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "DNGPrivateData", NULL},
+    {TIFFTAG_MAKERNOTESAFETY, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MakerNoteSafety", NULL},
+    {TIFFTAG_CALIBRATIONILLUMINANT1, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CalibrationIlluminant1", NULL},
+    {TIFFTAG_CALIBRATIONILLUMINANT2, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CalibrationIlluminant2", NULL},
+    {TIFFTAG_RAWDATAUNIQUEID, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RawDataUniqueID", NULL},
+    {TIFFTAG_ORIGINALRAWFILENAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OriginalRawFileName", NULL},
+    {TIFFTAG_ORIGINALRAWFILEDATA, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OriginalRawFileData", NULL},
+    {TIFFTAG_ACTIVEAREA, 4, 4, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ActiveArea", NULL},
+    {TIFFTAG_MASKEDAREAS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "MaskedAreas", NULL},
+    {TIFFTAG_ASSHOTICCPROFILE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AsShotICCProfile", NULL},
+    {TIFFTAG_ASSHOTPREPROFILEMATRIX, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AsShotPreProfileMatrix", NULL},
+    {TIFFTAG_CURRENTICCPROFILE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CurrentICCProfile", NULL},
+    {TIFFTAG_CURRENTPREPROFILEMATRIX, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CurrentPreProfileMatrix", NULL},
+    {TIFFTAG_PERSAMPLE, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "PerSample", NULL},
+#if 0
+    /* TODO: revert above #if 0 for TIFF 4.6.0 */
+
+    /* begin DNG 1.2.0.0 tags */
+    {TIFFTAG_COLORIMETRICREFERENCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ColorimetricReference", NULL},
+    {TIFFTAG_CAMERACALIBRATIONSIGNATURE, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CameraCalibrationSignature", NULL},
+    {TIFFTAG_PROFILECALIBRATIONSIGNATURE, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileCalibrationSignature", NULL},
+    {TIFFTAG_EXTRACAMERAPROFILES, -1, -1, TIFF_IFD8, 0, TIFF_SETGET_C16_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ExtraCameraProfiles", NULL},
+    {TIFFTAG_ASSHOTPROFILENAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AsShotProfileName", NULL},
+    {TIFFTAG_NOISEREDUCTIONAPPLIED, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "NoiseReductionApplied", NULL},
+    {TIFFTAG_PROFILENAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileName", NULL},
+    {TIFFTAG_PROFILEHUESATMAPDIMS, 3, 3, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ProfileHueSatMapDims", NULL},
+    {TIFFTAG_PROFILEHUESATMAPDATA1, -1, -1, TIFF_FLOAT, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileHueSatMapData1", NULL},
+    {TIFFTAG_PROFILEHUESATMAPDATA2, -1, -1, TIFF_FLOAT, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileHueSatMapData2", NULL},
+    {TIFFTAG_PROFILETONECURVE, -1, -1, TIFF_FLOAT, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileToneCurve", NULL},
+    {TIFFTAG_PROFILEEMBEDPOLICY, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ProfileEmbedPolicy", NULL},
+    {TIFFTAG_PROFILECOPYRIGHT, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileCopyright", NULL},
+    {TIFFTAG_FORWARDMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ForwardMatrix1", NULL},
+    {TIFFTAG_FORWARDMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ForwardMatrix2", NULL},
+    {TIFFTAG_PREVIEWAPPLICATIONNAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "PreviewApplicationName", NULL},
+    {TIFFTAG_PREVIEWAPPLICATIONVERSION, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "PreviewApplicationVersion", NULL},
+    {TIFFTAG_PREVIEWSETTINGSNAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "PreviewSettingsName", NULL},
+    {TIFFTAG_PREVIEWSETTINGSDIGEST, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PreviewSettingsDigest", NULL},
+    {TIFFTAG_PREVIEWCOLORSPACE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PreviewColorSpace", NULL},
+    {TIFFTAG_PREVIEWDATETIME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PreviewDateTime", NULL},
+    {TIFFTAG_RAWIMAGEDIGEST, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RawImageDigest", NULL},
+    {TIFFTAG_ORIGINALRAWFILEDIGEST, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OriginalRawFileDigest", NULL},
+    {TIFFTAG_SUBTILEBLOCKSIZE, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubTileBlockSize", NULL},
+    {TIFFTAG_ROWINTERLEAVEFACTOR, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RowInterleaveFactor", NULL},
+    {TIFFTAG_PROFILELOOKTABLEDIMS, 3, 3, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ProfileLookTableDims", NULL},
+    {TIFFTAG_PROFILELOOKTABLEDATA, -1, -1, TIFF_FLOAT, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileLookTableData", NULL},
+    /* begin DNG 1.3.0.0 tags */
+    {TIFFTAG_OPCODELIST1, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OpcodeList1", NULL},
+    {TIFFTAG_OPCODELIST2, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OpcodeList2", NULL},
+    {TIFFTAG_OPCODELIST3, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OpcodeList3", NULL},
+    {TIFFTAG_NOISEPROFILE, -1, -1, TIFF_DOUBLE, 0, TIFF_SETGET_C16_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "NoiseProfile", NULL},
+    /* begin DNG 1.4.0.0 tags */
+    {TIFFTAG_DEFAULTUSERCROP, 4, 4, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DefaultUserCrop", NULL},
+    {TIFFTAG_DEFAULTBLACKRENDER, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DefaultBlackRender", NULL},
+    {TIFFTAG_BASELINEEXPOSUREOFFSET, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BaselineExposureOffset", NULL},
+    {TIFFTAG_PROFILELOOKTABLEENCODING, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ProfileLookTableEncoding", NULL},
+    {TIFFTAG_PROFILEHUESATMAPENCODING, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ProfileHueSatMapEncoding", NULL},
+    {TIFFTAG_ORIGINALDEFAULTFINALSIZE, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OriginalDefaultFinalSize", NULL},
+    {TIFFTAG_ORIGINALBESTQUALITYFINALSIZE, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OriginalBestQualityFinalSize", NULL},
+    {TIFFTAG_ORIGINALDEFAULTCROPSIZE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OriginalDefaultCropSize", NULL}, /* could also be rational */
+    {TIFFTAG_NEWRAWIMAGEDIGEST, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "NewRawImageDigest", NULL},
+    {TIFFTAG_RAWTOPREVIEWGAIN, 1, 1, TIFF_DOUBLE, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RawToPreviewGain", NULL},
+    /* begin DNG 1.5.0.0 tags */
+    {TIFFTAG_DEPTHFORMAT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DepthFormat", NULL},
+    {TIFFTAG_DEPTHNEAR, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DepthNear", NULL},
+    {TIFFTAG_DEPTHFAR, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DepthFar", NULL},
+    {TIFFTAG_DEPTHUNITS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DepthUnits", NULL},
+    {TIFFTAG_DEPTHMEASURETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DepthMeasureType", NULL},
+    {TIFFTAG_ENHANCEPARAMS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EnhanceParams", NULL},
+    /* begin DNG 1.6.0.0 tags */
+    {TIFFTAG_PROFILEGAINTABLEMAP, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileGainTableMap", NULL},
+    {TIFFTAG_SEMANTICNAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SemanticName", NULL},
+    {TIFFTAG_SEMANTICINSTANCEID, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SemanticInstanceID", NULL},
+    {TIFFTAG_MASKSUBAREA, 4, 4, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MaskSubArea", NULL},
+    {TIFFTAG_RGBTABLES, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "RGBTables", NULL},
+    {TIFFTAG_CALIBRATIONILLUMINANT3, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CalibrationIlluminant3", NULL},
+    {TIFFTAG_COLORMATRIX3, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ColorMatrix3", NULL},
+    {TIFFTAG_CAMERACALIBRATION3, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CameraCalibration3", NULL},
+    {TIFFTAG_REDUCTIONMATRIX3, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ReductionMatrix3", NULL},
+    {TIFFTAG_PROFILEHUESATMAPDATA3, -1, -1, TIFF_FLOAT, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProfileHueSatMapData3", NULL},
+    {TIFFTAG_FORWARDMATRIX3, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ForwardMatrix3", NULL},
+    {TIFFTAG_ILLUMINANTDATA1, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "IlluminantData1", NULL},
+    {TIFFTAG_ILLUMINANTDATA2, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "IlluminantData2", NULL},
+    {TIFFTAG_ILLUMINANTDATA3, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "IlluminantData3", NULL},
     /* end DNG tags */
+    /* begin TIFF/EP tags */
+    {TIFFTAG_EP_CFAREPEATPATTERNDIM, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP CFARepeatPatternDim", NULL},
+    {TIFFTAG_EP_CFAPATTERN, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP CFAPattern", NULL},
+    /* TIFFTAG_EP_BATTERYLEVEL can be RATIONAL or ASCII.
+     * LibTiff defines it as ASCII and converts RATIONAL to an ASCII string. */
+    {TIFFTAG_EP_BATTERYLEVEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP BatteryLevel", NULL},
+    {TIFFTAG_EP_INTERLACE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP Interlace", NULL},
+    /* TIFFTAG_EP_IPTC_NAA and TIFFTAG_RICHTIFFIPTC share the same tag number (33723)
+     *   LibTIFF type is UNDEFINED or BYTE, but often times incorrectly specified as LONG, because TIFF/EP (ISO/DIS 12234-2) specifies type LONG or ASCII. */
+    {TIFFTAG_EP_TIMEZONEOFFSET, -1, -1, TIFF_SSHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP TimeZoneOffset", NULL},
+    {TIFFTAG_EP_SELFTIMERMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP SelfTimerMode", NULL},
+    {TIFFTAG_EP_FLASHENERGY, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP FlashEnergy", NULL},
+    {TIFFTAG_EP_SPATIALFREQUENCYRESPONSE, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP SpatialFrequencyResponse", NULL},
+    {TIFFTAG_EP_NOISE, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP Noise", NULL},
+    {TIFFTAG_EP_FOCALPLANEXRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP FocalPlaneXResolution", NULL},
+    {TIFFTAG_EP_FOCALPLANEYRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP FocalPlaneYResolution", NULL},
+    {TIFFTAG_EP_FOCALPLANERESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP FocalPlaneResolutionUnit", NULL},
+    {TIFFTAG_EP_IMAGENUMBER, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP ImageNumber", NULL}, /* or SHORT */
+    {TIFFTAG_EP_SECURITYCLASSIFICATION, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP SecurityClassification", NULL},
+    {TIFFTAG_EP_IMAGEHISTORY, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP ImageHistory", NULL},
+    {TIFFTAG_EP_EXPOSUREINDEX, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP ExposureIndex", NULL},
+    {TIFFTAG_EP_STANDARDID, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP StandardId", NULL},
+    {TIFFTAG_EP_SENSINGMETHOD, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP SensingMethod", NULL},
+    /* TIFF/EP tags equivalent to EXIF tags, sometimes defined differently. */
+    {TIFFTAG_EP_EXPOSURETIME, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP ExposureTime", NULL}, /*N=1 or 2 */
+    {TIFFTAG_EP_FNUMBER, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP FNumber", NULL},
+    {TIFFTAG_EP_EXPOSUREPROGRAM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP ExposureProgram", NULL},
+    {TIFFTAG_EP_SPECTRALSENSITIVITY, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP SpectralSensitivity", NULL},
+    {TIFFTAG_EP_ISOSPEEDRATINGS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP ISOSpeedRatings", NULL},
+    {TIFFTAG_EP_OECF, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP OptoelectricConversionFactor", NULL},
+    {TIFFTAG_EP_DATETIMEORIGINAL, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP DateTimeOriginal", NULL},
+    {TIFFTAG_EP_COMPRESSEDBITSPERPIXEL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP CompressedBitsPerPixel", NULL},
+    {TIFFTAG_EP_SHUTTERSPEEDVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP ShutterSpeedValue", NULL},
+    {TIFFTAG_EP_APERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP ApertureValue", NULL},
+    {TIFFTAG_EP_BRIGHTNESSVALUE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP BrightnessValue", NULL},
+    {TIFFTAG_EP_EXPOSUREBIASVALUE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP ExposureBiasValue", NULL}, /*N=1 or 2 */
+    {TIFFTAG_EP_MAXAPERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP MaxApertureValue", NULL},
+    {TIFFTAG_EP_SUBJECTDISTANCE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP SubjectDistance", NULL},
+    {TIFFTAG_EP_METERINGMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP MeteringMode", NULL},
+    {TIFFTAG_EP_LIGHTSOURCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP LightSource", NULL},
+    {TIFFTAG_EP_FLASH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "EP Flash", NULL},
+    {TIFFTAG_EP_FOCALLENGTH, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP FocalLength", NULL},
+    {TIFFTAG_EP_SUBJECTLOCATION, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "EP SubjectLocation", NULL},
+    /* end TIFF/EP tags */
+#endif
     /* begin TIFF/FX tags */
-    {TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "Indexed", NULL},
-    {TIFFTAG_GLOBALPARAMETERSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "GlobalParametersIFD", NULL},
-    {TIFFTAG_PROFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ProfileType", NULL},
-    {TIFFTAG_FAXPROFILE, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "FaxProfile", NULL},
-    {TIFFTAG_CODINGMETHODS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CodingMethods", NULL},
-    {TIFFTAG_VERSIONYEAR, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "VersionYear", NULL},
-    {TIFFTAG_MODENUMBER, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ModeNumber", NULL},
-    {TIFFTAG_DECODE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "Decode", NULL},
-    {TIFFTAG_IMAGEBASECOLOR, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ImageBaseColor", NULL},
-    {TIFFTAG_T82OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "T82Options", NULL},
-    {TIFFTAG_STRIPROWCOUNTS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "StripRowCounts", NULL},
-    {TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ImageLayer", NULL},
+    {TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Indexed", NULL},
+    {TIFFTAG_GLOBALPARAMETERSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GlobalParametersIFD", NULL},
+    {TIFFTAG_PROFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ProfileType", NULL},
+    {TIFFTAG_FAXPROFILE, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FaxProfile", NULL},
+    {TIFFTAG_CODINGMETHODS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CodingMethods", NULL},
+    {TIFFTAG_VERSIONYEAR, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "VersionYear", NULL},
+    {TIFFTAG_MODENUMBER, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ModeNumber", NULL},
+    {TIFFTAG_DECODE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "Decode", NULL},
+    {TIFFTAG_IMAGEBASECOLOR, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ImageBaseColor", NULL},
+    {TIFFTAG_T82OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "T82Options", NULL},
+    {TIFFTAG_STRIPROWCOUNTS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "StripRowCounts", NULL},
+    {TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageLayer", NULL},
     /* end TIFF/FX tags */
     /* begin pseudo tags */
 };
@@ -416,181 +348,91 @@
  * EXIF tags  (Version 2.31, July 2016 plus version 2.32 May 2019)
  */
 static const TIFFField exifFields[] = {
-    {EXIFTAG_EXPOSURETIME, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureTime", NULL},
-    {EXIFTAG_FNUMBER, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FNumber", NULL},
-    {EXIFTAG_EXPOSUREPROGRAM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureProgram", NULL},
-    {EXIFTAG_SPECTRALSENSITIVITY, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SpectralSensitivity", NULL},
-    {EXIFTAG_ISOSPEEDRATINGS, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ISOSpeedRatings", NULL},
-    {EXIFTAG_OECF, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OptoelectricConversionFactor",
-     NULL},
-    {EXIFTAG_SENSITIVITYTYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SensitivityType", NULL},
-    {EXIFTAG_STANDARDOUTPUTSENSITIVITY, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "StandardOutputSensitivity",
-     NULL},
-    {EXIFTAG_RECOMMENDEDEXPOSUREINDEX, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RecommendedExposureIndex",
-     NULL},
-    {EXIFTAG_ISOSPEED, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ISOSpeed", NULL},
-    {EXIFTAG_ISOSPEEDLATITUDEYYY, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ISOSpeedLatitudeyyy", NULL},
-    {EXIFTAG_ISOSPEEDLATITUDEZZZ, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ISOSpeedLatitudezzz", NULL},
-    {EXIFTAG_EXIFVERSION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExifVersion", NULL},
-    {EXIFTAG_DATETIMEORIGINAL, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTimeOriginal", NULL},
-    {EXIFTAG_DATETIMEDIGITIZED, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTimeDigitized", NULL},
-    {EXIFTAG_OFFSETTIME, 7, 7, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OffsetTime", NULL},
-    {EXIFTAG_OFFSETTIMEORIGINAL, 7, 7, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OffsetTimeOriginal", NULL},
-    {EXIFTAG_OFFSETTIMEDIGITIZED, 7, 7, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OffsetTimeDigitized", NULL},
-    {EXIFTAG_COMPONENTSCONFIGURATION, 4, 4, TIFF_UNDEFINED, 0,
-     TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0,
-     "ComponentsConfiguration", NULL},
-    {EXIFTAG_COMPRESSEDBITSPERPIXEL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CompressedBitsPerPixel", NULL},
-    {EXIFTAG_SHUTTERSPEEDVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ShutterSpeedValue", NULL},
-    {EXIFTAG_APERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ApertureValue", NULL},
-    {EXIFTAG_BRIGHTNESSVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BrightnessValue", NULL},
-    {EXIFTAG_EXPOSUREBIASVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureBiasValue", NULL},
-    {EXIFTAG_MAXAPERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MaxApertureValue", NULL},
-    /*--: EXIFTAG_SUBJECTDISTANCE: LibTiff returns value of "-1" if numerator
-     * equals 4294967295 (0xFFFFFFFF) to indicate infinite distance! However,
-     * there are two other EXIF tags where numerator indicates a special value
-     * and six other cases where the denominator indicates special values, which
-     * are not treated within LibTiff!! */
-    {EXIFTAG_SUBJECTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectDistance", NULL},
-    {EXIFTAG_METERINGMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MeteringMode", NULL},
-    {EXIFTAG_LIGHTSOURCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LightSource", NULL},
-    {EXIFTAG_FLASH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Flash", NULL},
-    {EXIFTAG_FOCALLENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalLength", NULL},
-    {EXIFTAG_SUBJECTAREA, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "SubjectArea", NULL},
-    {EXIFTAG_MAKERNOTE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "MakerNote", NULL},
-    {EXIFTAG_USERCOMMENT, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "UserComment", NULL},
-    {EXIFTAG_SUBSECTIME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTime", NULL},
-    {EXIFTAG_SUBSECTIMEORIGINAL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTimeOriginal", NULL},
-    {EXIFTAG_SUBSECTIMEDIGITIZED, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTimeDigitized", NULL},
-    {EXIFTAG_TEMPERATURE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Temperature", NULL},
-    {EXIFTAG_HUMIDITY, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Humidity", NULL},
-    {EXIFTAG_PRESSURE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Pressure", NULL},
-    {EXIFTAG_WATERDEPTH, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WaterDepth", NULL},
-    {EXIFTAG_ACCELERATION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Acceleration", NULL},
-    {EXIFTAG_CAMERAELEVATIONANGLE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraElevationAngle", NULL},
-    {EXIFTAG_FLASHPIXVERSION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FlashpixVersion", NULL},
-    {EXIFTAG_COLORSPACE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ColorSpace", NULL},
-    {EXIFTAG_PIXELXDIMENSION, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PixelXDimension", NULL},
-    {EXIFTAG_PIXELYDIMENSION, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PixelYDimension", NULL},
-    {EXIFTAG_RELATEDSOUNDFILE, 13, 13, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RelatedSoundFile", NULL},
-    {EXIFTAG_FLASHENERGY, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FlashEnergy", NULL},
-    {EXIFTAG_SPATIALFREQUENCYRESPONSE, -1, -1, TIFF_UNDEFINED, 0,
-     TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1,
-     "SpatialFrequencyResponse", NULL},
-    {EXIFTAG_FOCALPLANEXRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneXResolution", NULL},
-    {EXIFTAG_FOCALPLANEYRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneYResolution", NULL},
-    {EXIFTAG_FOCALPLANERESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneResolutionUnit",
-     NULL},
-    {EXIFTAG_SUBJECTLOCATION, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectLocation", NULL},
-    {EXIFTAG_EXPOSUREINDEX, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureIndex", NULL},
-    {EXIFTAG_SENSINGMETHOD, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SensingMethod", NULL},
-    {EXIFTAG_FILESOURCE, 1, 1, TIFF_UNDEFINED, 0, TIFF_SETGET_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FileSource", NULL},
-    {EXIFTAG_SCENETYPE, 1, 1, TIFF_UNDEFINED, 0, TIFF_SETGET_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SceneType", NULL},
-    {EXIFTAG_CFAPATTERN, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CFAPattern", NULL},
-    {EXIFTAG_CUSTOMRENDERED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CustomRendered", NULL},
-    {EXIFTAG_EXPOSUREMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureMode", NULL},
-    {EXIFTAG_WHITEBALANCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WhiteBalance", NULL},
-    {EXIFTAG_DIGITALZOOMRATIO, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DigitalZoomRatio", NULL},
-    {EXIFTAG_FOCALLENGTHIN35MMFILM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalLengthIn35mmFilm", NULL},
-    {EXIFTAG_SCENECAPTURETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SceneCaptureType", NULL},
-    {EXIFTAG_GAINCONTROL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GainControl", NULL},
-    {EXIFTAG_CONTRAST, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Contrast", NULL},
-    {EXIFTAG_SATURATION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Saturation", NULL},
-    {EXIFTAG_SHARPNESS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Sharpness", NULL},
-    {EXIFTAG_DEVICESETTINGDESCRIPTION, -1, -1, TIFF_UNDEFINED, 0,
-     TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1,
-     "DeviceSettingDescription", NULL},
-    {EXIFTAG_SUBJECTDISTANCERANGE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectDistanceRange", NULL},
-    {EXIFTAG_IMAGEUNIQUEID, 33, 33, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageUniqueID", NULL},
-    {EXIFTAG_CAMERAOWNERNAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraOwnerName", NULL},
-    {EXIFTAG_BODYSERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BodySerialNumber", NULL},
-    {EXIFTAG_LENSSPECIFICATION, 4, 4, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensSpecification", NULL},
-    {EXIFTAG_LENSMAKE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensMake", NULL},
-    {EXIFTAG_LENSMODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensModel", NULL},
-    {EXIFTAG_LENSSERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensSerialNumber", NULL},
-    {EXIFTAG_GAMMA, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Gamma", NULL},
-    {EXIFTAG_COMPOSITEIMAGE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CompositeImage", NULL},
-    {EXIFTAG_SOURCEIMAGENUMBEROFCOMPOSITEIMAGE, 2, 2, TIFF_SHORT, 0,
-     TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0,
-     "SourceImageNumberOfCompositeImage", NULL},
-    {EXIFTAG_SOURCEEXPOSURETIMESOFCOMPOSITEIMAGE, -1, -1, TIFF_UNDEFINED, 0,
-     TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1,
+    {EXIFTAG_EXPOSURETIME, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureTime", NULL},
+    {EXIFTAG_FNUMBER, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FNumber", NULL},
+    {EXIFTAG_EXPOSUREPROGRAM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureProgram", NULL},
+    {EXIFTAG_SPECTRALSENSITIVITY, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SpectralSensitivity", NULL},
+    /* After EXIF 2.2.1 ISOSpeedRatings is named PhotographicSensitivity. In addition, while "Count=Any", only 1 count should be used. */
+    {EXIFTAG_ISOSPEEDRATINGS, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ISOSpeedRatings", NULL},
+    {EXIFTAG_OECF, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OptoelectricConversionFactor", NULL},
+    {EXIFTAG_SENSITIVITYTYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SensitivityType", NULL},
+    {EXIFTAG_STANDARDOUTPUTSENSITIVITY, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "StandardOutputSensitivity", NULL},
+    {EXIFTAG_RECOMMENDEDEXPOSUREINDEX, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RecommendedExposureIndex", NULL},
+    {EXIFTAG_ISOSPEED, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ISOSpeed", NULL},
+    {EXIFTAG_ISOSPEEDLATITUDEYYY, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ISOSpeedLatitudeyyy", NULL},
+    {EXIFTAG_ISOSPEEDLATITUDEZZZ, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ISOSpeedLatitudezzz", NULL},
+    {EXIFTAG_EXIFVERSION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExifVersion", NULL},
+    {EXIFTAG_DATETIMEORIGINAL, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTimeOriginal", NULL},
+    {EXIFTAG_DATETIMEDIGITIZED, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTimeDigitized", NULL},
+    {EXIFTAG_OFFSETTIME, 7, 7, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OffsetTime", NULL},
+    {EXIFTAG_OFFSETTIMEORIGINAL, 7, 7, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OffsetTimeOriginal", NULL},
+    {EXIFTAG_OFFSETTIMEDIGITIZED, 7, 7, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "OffsetTimeDigitized", NULL},
+    {EXIFTAG_COMPONENTSCONFIGURATION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ComponentsConfiguration", NULL},
+    {EXIFTAG_COMPRESSEDBITSPERPIXEL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CompressedBitsPerPixel", NULL},
+    {EXIFTAG_SHUTTERSPEEDVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ShutterSpeedValue", NULL},
+    {EXIFTAG_APERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ApertureValue", NULL},
+    {EXIFTAG_BRIGHTNESSVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BrightnessValue", NULL},
+    {EXIFTAG_EXPOSUREBIASVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureBiasValue", NULL},
+    {EXIFTAG_MAXAPERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MaxApertureValue", NULL},
+    /*--: EXIFTAG_SUBJECTDISTANCE: LibTiff returns value of "-1" if numerator equals 4294967295 (0xFFFFFFFF) to indicate infinite distance! 
+     *    However, there are two other EXIF tags where numerator indicates a special value and six other cases where the denominator indicates special values,
+     *    which are not treated within LibTiff!! */
+    {EXIFTAG_SUBJECTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectDistance", NULL},
+    {EXIFTAG_METERINGMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MeteringMode", NULL},
+    {EXIFTAG_LIGHTSOURCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LightSource", NULL},
+    {EXIFTAG_FLASH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Flash", NULL},
+    {EXIFTAG_FOCALLENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalLength", NULL},
+    {EXIFTAG_SUBJECTAREA, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "SubjectArea", NULL},
+    {EXIFTAG_MAKERNOTE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "MakerNote", NULL},
+    {EXIFTAG_USERCOMMENT, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "UserComment", NULL},
+    {EXIFTAG_SUBSECTIME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTime", NULL},
+    {EXIFTAG_SUBSECTIMEORIGINAL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTimeOriginal", NULL},
+    {EXIFTAG_SUBSECTIMEDIGITIZED, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTimeDigitized", NULL},
+    {EXIFTAG_TEMPERATURE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Temperature", NULL},
+    {EXIFTAG_HUMIDITY, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Humidity", NULL},
+    {EXIFTAG_PRESSURE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Pressure", NULL},
+    {EXIFTAG_WATERDEPTH, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WaterDepth", NULL},
+    {EXIFTAG_ACCELERATION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Acceleration", NULL},
+    {EXIFTAG_CAMERAELEVATIONANGLE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraElevationAngle", NULL},
+    {EXIFTAG_FLASHPIXVERSION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FlashpixVersion", NULL},
+    {EXIFTAG_COLORSPACE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ColorSpace", NULL},
+    {EXIFTAG_PIXELXDIMENSION, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PixelXDimension", NULL},
+    {EXIFTAG_PIXELYDIMENSION, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PixelYDimension", NULL},
+    {EXIFTAG_RELATEDSOUNDFILE, 13, 13, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RelatedSoundFile", NULL},
+    {EXIFTAG_FLASHENERGY, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FlashEnergy", NULL},
+    {EXIFTAG_SPATIALFREQUENCYRESPONSE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "SpatialFrequencyResponse", NULL},
+    {EXIFTAG_FOCALPLANEXRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneXResolution", NULL},
+    {EXIFTAG_FOCALPLANEYRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneYResolution", NULL},
+    {EXIFTAG_FOCALPLANERESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneResolutionUnit", NULL},
+    {EXIFTAG_SUBJECTLOCATION, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectLocation", NULL},
+    {EXIFTAG_EXPOSUREINDEX, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureIndex", NULL},
+    {EXIFTAG_SENSINGMETHOD, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SensingMethod", NULL},
+    {EXIFTAG_FILESOURCE, 1, 1, TIFF_UNDEFINED, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FileSource", NULL},
+    {EXIFTAG_SCENETYPE, 1, 1, TIFF_UNDEFINED, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SceneType", NULL},
+    {EXIFTAG_CFAPATTERN, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CFAPattern", NULL},
+    {EXIFTAG_CUSTOMRENDERED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CustomRendered", NULL},
+    {EXIFTAG_EXPOSUREMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureMode", NULL},
+    {EXIFTAG_WHITEBALANCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WhiteBalance", NULL},
+    {EXIFTAG_DIGITALZOOMRATIO, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DigitalZoomRatio", NULL},
+    {EXIFTAG_FOCALLENGTHIN35MMFILM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalLengthIn35mmFilm", NULL},
+    {EXIFTAG_SCENECAPTURETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SceneCaptureType", NULL},
+    {EXIFTAG_GAINCONTROL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GainControl", NULL},
+    {EXIFTAG_CONTRAST, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Contrast", NULL},
+    {EXIFTAG_SATURATION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Saturation", NULL},
+    {EXIFTAG_SHARPNESS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Sharpness", NULL},
+    {EXIFTAG_DEVICESETTINGDESCRIPTION, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "DeviceSettingDescription", NULL},
+    {EXIFTAG_SUBJECTDISTANCERANGE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectDistanceRange", NULL},
+    {EXIFTAG_IMAGEUNIQUEID, 33, 33, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageUniqueID", NULL},
+    {EXIFTAG_CAMERAOWNERNAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraOwnerName", NULL},
+    {EXIFTAG_BODYSERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BodySerialNumber", NULL},
+    {EXIFTAG_LENSSPECIFICATION, 4, 4, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensSpecification", NULL},
+    {EXIFTAG_LENSMAKE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensMake", NULL},
+    {EXIFTAG_LENSMODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensModel", NULL},
+    {EXIFTAG_LENSSERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LensSerialNumber", NULL},
+    {EXIFTAG_GAMMA, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Gamma", NULL},
+    {EXIFTAG_COMPOSITEIMAGE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CompositeImage", NULL},
+    {EXIFTAG_SOURCEIMAGENUMBEROFCOMPOSITEIMAGE, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SourceImageNumberOfCompositeImage", NULL},
+    {EXIFTAG_SOURCEEXPOSURETIMESOFCOMPOSITEIMAGE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1,
      "SourceExposureTimesOfCompositeImage", NULL}};
 /*
  * EXIF-GPS tags  (Version 2.31, July 2016; nothing changed for version 2.32 May
@@ -598,79 +440,44 @@
  */
 
 static const TIFFField gpsFields[] = {
-    /*  For the GPS tag definitions in gpsFields[] the standard definition for
-     *Rationals is TIFF_SETGET_DOUBLE and TIFF_SETGET_C0_FLOAT.
-     *-- ATTENTION: After the upgrade with Rational2Double, the GPSTAG values
-     *can now be written and also read in double precision! In order to achieve
-     *double precision for GPS tags: Standard definitions for GPSTAG is kept to
-     *TIFF_SETGET_DOUBLE and TIFF_SETGET_C0_FLOAT is changed to
-     *TIFF_SETGET_C0_DOUBLE.
+    /*  For the GPS tag definitions in gpsFields[] the standard definition for Rationals is TIFF_SETGET_DOUBLE and TIFF_SETGET_C0_FLOAT.
+     *-- ATTENTION: After the upgrade with Rational2Double, the GPSTAG values can now be written and also read in double precision!
+     *              In order to achieve double precision for GPS tags: Standard definitions for GPSTAG is kept to TIFF_SETGET_DOUBLE
+     *              and TIFF_SETGET_C0_FLOAT is changed to TIFF_SETGET_C0_DOUBLE.
      */
-    {GPSTAG_VERSIONID, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8,
-     TIFF_SETGET_UINT8, FIELD_CUSTOM, 1, 0, "VersionID", NULL},
-    {GPSTAG_LATITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LatitudeRef", NULL},
-    {GPSTAG_LATITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Latitude", NULL},
-    {GPSTAG_LONGITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LongitudeRef", NULL},
-    {GPSTAG_LONGITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Longitude", NULL},
-    {GPSTAG_ALTITUDEREF, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "AltitudeRef", NULL},
-    {GPSTAG_ALTITUDE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Altitude", NULL},
-    {GPSTAG_TIMESTAMP, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TimeStamp", NULL},
-    {GPSTAG_SATELLITES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Satellites", NULL},
-    {GPSTAG_STATUS, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Status", NULL},
-    {GPSTAG_MEASUREMODE, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MeasureMode", NULL},
-    {GPSTAG_DOP, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DOP", NULL},
-    {GPSTAG_SPEEDREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SpeedRef", NULL},
-    {GPSTAG_SPEED, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Speed", NULL},
-    {GPSTAG_TRACKREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TrackRef", NULL},
-    {GPSTAG_TRACK, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Track", NULL},
-    {GPSTAG_IMGDIRECTIONREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImgDirectionRef", NULL},
-    {GPSTAG_IMGDIRECTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImgDirection", NULL},
-    {GPSTAG_MAPDATUM, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MapDatum", NULL},
-    {GPSTAG_DESTLATITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLatitudeRef", NULL},
-    {GPSTAG_DESTLATITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLatitude", NULL},
-    {GPSTAG_DESTLONGITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLongitudeRef", NULL},
-    {GPSTAG_DESTLONGITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLongitude", NULL},
-    {GPSTAG_DESTBEARINGREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestBearingRef", NULL},
-    {GPSTAG_DESTBEARING, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestBearing", NULL},
-    {GPSTAG_DESTDISTANCEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestDistanceRef", NULL},
-    {GPSTAG_DESTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestDistance", NULL},
-    {GPSTAG_PROCESSINGMETHOD, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProcessingMethod", NULL},
-    {GPSTAG_AREAINFORMATION, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AreaInformation", NULL},
-    {GPSTAG_DATESTAMP, 11, 11, TIFF_ASCII, 0, TIFF_SETGET_ASCII,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateStamp", NULL},
-    {GPSTAG_DIFFERENTIAL, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Differential", NULL},
-    {GPSTAG_GPSHPOSITIONINGERROR, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE,
-     TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "HorizontalPositioningError",
-     NULL}};
+    {GPSTAG_VERSIONID, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UINT8, FIELD_CUSTOM, 1, 0, "VersionID", NULL},
+    {GPSTAG_LATITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LatitudeRef", NULL},
+    {GPSTAG_LATITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Latitude", NULL},
+    {GPSTAG_LONGITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LongitudeRef", NULL},
+    {GPSTAG_LONGITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Longitude", NULL},
+    {GPSTAG_ALTITUDEREF, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "AltitudeRef", NULL},
+    {GPSTAG_ALTITUDE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Altitude", NULL},
+    {GPSTAG_TIMESTAMP, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TimeStamp", NULL},
+    {GPSTAG_SATELLITES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Satellites", NULL},
+    {GPSTAG_STATUS, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Status", NULL},
+    {GPSTAG_MEASUREMODE, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MeasureMode", NULL},
+    {GPSTAG_DOP, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DOP", NULL},
+    {GPSTAG_SPEEDREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SpeedRef", NULL},
+    {GPSTAG_SPEED, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Speed", NULL},
+    {GPSTAG_TRACKREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TrackRef", NULL},
+    {GPSTAG_TRACK, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Track", NULL},
+    {GPSTAG_IMGDIRECTIONREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImgDirectionRef", NULL},
+    {GPSTAG_IMGDIRECTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImgDirection", NULL},
+    {GPSTAG_MAPDATUM, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MapDatum", NULL},
+    {GPSTAG_DESTLATITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLatitudeRef", NULL},
+    {GPSTAG_DESTLATITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLatitude", NULL},
+    {GPSTAG_DESTLONGITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLongitudeRef", NULL},
+    {GPSTAG_DESTLONGITUDE, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestLongitude", NULL},
+    {GPSTAG_DESTBEARINGREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestBearingRef", NULL},
+    {GPSTAG_DESTBEARING, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestBearing", NULL},
+    {GPSTAG_DESTDISTANCEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestDistanceRef", NULL},
+    {GPSTAG_DESTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DestDistance", NULL},
+    {GPSTAG_PROCESSINGMETHOD, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ProcessingMethod", NULL},
+    {GPSTAG_AREAINFORMATION, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "AreaInformation", NULL},
+    {GPSTAG_DATESTAMP, 11, 11, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateStamp", NULL},
+    {GPSTAG_DIFFERENTIAL, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Differential", NULL},
+    {GPSTAG_GPSHPOSITIONINGERROR, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "HorizontalPositioningError", NULL}};
+/* clang-format on */ /* was off for better readability of tag comments */
 
 static const TIFFFieldArray tiffFieldArray = {
     tfiatImage, 0, TIFFArrayCount(tiffFields), (TIFFField *)tiffFields};
@@ -1039,8 +846,8 @@
     const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY);
     if (!fip)
     {
-        TIFFErrorExtR(tif, "TIFFFieldWithTag",
-                      "Internal error, unknown tag 0x%x", (unsigned int)tag);
+        TIFFWarningExtR(tif, "TIFFFieldWithTag", "Warning, unknown tag 0x%x",
+                        (unsigned int)tag);
     }
     return (fip);
 }
@@ -1050,8 +857,8 @@
     const TIFFField *fip = _TIFFFindFieldByName(tif, field_name, TIFF_ANY);
     if (!fip)
     {
-        TIFFErrorExtR(tif, "TIFFFieldWithName",
-                      "Internal error, unknown tag %s", field_name);
+        TIFFWarningExtR(tif, "TIFFFieldWithName", "Warning, unknown tag %s",
+                        field_name);
     }
     return (fip);
 }
diff --git a/third_party/libtiff/tif_dirread.c b/third_party/libtiff/tif_dirread.c
index 4c3b756..838f64d 100644
--- a/third_party/libtiff/tif_dirread.c
+++ b/third_party/libtiff/tif_dirread.c
@@ -144,7 +144,11 @@
                                          float *value);
 static enum TIFFReadDirEntryErr
 TIFFReadDirEntryCheckedDouble(TIFF *tif, TIFFDirEntry *direntry, double *value);
-
+#if 0
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedRationalDirect(TIFF *tif, TIFFDirEntry *direntry,
+                                      TIFFRational_t *value);
+#endif
 static enum TIFFReadDirEntryErr
 TIFFReadDirEntryCheckRangeByteSbyte(int8_t value);
 static enum TIFFReadDirEntryErr
@@ -3469,6 +3473,49 @@
     return (TIFFReadDirEntryErrOk);
 }
 
+#if 0
+static enum TIFFReadDirEntryErr
+TIFFReadDirEntryCheckedRationalDirect(TIFF *tif, TIFFDirEntry *direntry,
+                                      TIFFRational_t *value)
+{ /*--: SetGetRATIONAL_directly:_CustomTag: Read rational (and signed rationals)
+     directly --*/
+    UInt64Aligned_t m;
+
+    assert(sizeof(double) == 8);
+    assert(sizeof(uint64_t) == 8);
+    assert(sizeof(uint32_t) == 4);
+
+    if (direntry->tdir_count != 1)
+        return (TIFFReadDirEntryErrCount);
+
+    if (direntry->tdir_type != TIFF_RATIONAL &&
+        direntry->tdir_type != TIFF_SRATIONAL)
+        return (TIFFReadDirEntryErrType);
+
+    if (!(tif->tif_flags & TIFF_BIGTIFF))
+    {
+        enum TIFFReadDirEntryErr err;
+        uint32_t offset = direntry->tdir_offset.toff_long;
+        if (tif->tif_flags & TIFF_SWAB)
+            TIFFSwabLong(&offset);
+        err = TIFFReadDirEntryData(tif, offset, 8, m.i);
+        if (err != TIFFReadDirEntryErrOk)
+            return (err);
+    }
+    else
+    {
+        m.l = direntry->tdir_offset.toff_long8;
+    }
+
+    if (tif->tif_flags & TIFF_SWAB)
+        TIFFSwabArrayOfLong(m.i, 2);
+
+    value->uNum = m.i[0];
+    value->uDenom = m.i[1];
+    return (TIFFReadDirEntryErrOk);
+} /*-- TIFFReadDirEntryCheckedRationalDirect() --*/
+#endif
+
 static void TIFFReadDirEntryCheckedFloat(TIFF *tif, TIFFDirEntry *direntry,
                                          float *value)
 {
@@ -4067,9 +4114,11 @@
 
     if (tif->tif_nextdiroff == 0)
     {
-        /* In this special case, tif_diroff needs also to be set to 0. */
+        /* In this special case, tif_diroff needs also to be set to 0.
+         * This is behind the last IFD, thus no checking or reading necessary.
+         */
         tif->tif_diroff = tif->tif_nextdiroff;
-        return 0; /* last offset, thus no checking necessary */
+        return 0;
     }
 
     nextdiroff = tif->tif_nextdiroff;
@@ -4523,7 +4572,52 @@
                     }
                 }
                 break;
-                    /* END REV 4.0 COMPATIBILITY */
+                /* END REV 4.0 COMPATIBILITY */
+#if 0
+                case TIFFTAG_EP_BATTERYLEVEL:
+                    /* TIFFTAG_EP_BATTERYLEVEL can be RATIONAL or ASCII.
+                     * LibTiff defines it as ASCII and converts RATIONAL to an
+                     * ASCII string. */
+                    switch (dp->tdir_type)
+                    {
+                        case TIFF_RATIONAL:
+                        {
+                            /* Read rational and convert to ASCII*/
+                            enum TIFFReadDirEntryErr err;
+                            TIFFRational_t rValue;
+                            err = TIFFReadDirEntryCheckedRationalDirect(
+                                tif, dp, &rValue);
+                            if (err != TIFFReadDirEntryErrOk)
+                            {
+                                fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                                TIFFReadDirEntryOutputErr(
+                                    tif, err, module,
+                                    fip ? fip->field_name : "unknown tagname",
+                                    1);
+                            }
+                            else
+                            {
+                                char szAux[32];
+                                snprintf(szAux, sizeof(szAux) - 1, "%d/%d",
+                                         rValue.uNum, rValue.uDenom);
+                                TIFFSetField(tif, dp->tdir_tag, szAux);
+                            }
+                        }
+                        break;
+                        case TIFF_ASCII:
+                            (void)TIFFFetchNormalTag(tif, dp, TRUE);
+                            break;
+                        default:
+                            fip = TIFFFieldWithTag(tif, dp->tdir_tag);
+                            TIFFWarningExtR(tif, module,
+                                            "Invalid data type for tag %s. "
+                                            "ASCII or RATIONAL expected",
+                                            fip ? fip->field_name
+                                                : "unknown tagname");
+                            break;
+                    }
+                    break;
+#endif
                 default:
                     (void)TIFFFetchNormalTag(tif, dp, TRUE);
                     break;
@@ -5139,6 +5233,8 @@
             } /*-- if (!dp->tdir_ignore) */
         }
     }
+    /* To be able to return from SubIFD or custom-IFD to main-IFD */
+    tif->tif_setdirectory_force_absolute = TRUE;
     if (dir)
         _TIFFfreeExt(tif, dir);
     return 1;
@@ -5456,7 +5552,8 @@
     }
 
     /* Arbitrary (hopefully big enough) limit */
-    if (tif->tif_dirnumber >= TIFF_MAX_DIR_COUNT)
+    if (TIFFHashSetSize(tif->tif_map_dir_offset_to_number) >=
+        TIFF_MAX_DIR_COUNT)
     {
         TIFFErrorExtR(tif, "_TIFFCheckDirNumberAndOffset",
                       "Cannot handle more than %u TIFF directories",
@@ -5489,8 +5586,6 @@
         return 0;
     }
 
-    tif->tif_dirnumber++;
-
     return 1;
 } /* --- _TIFFCheckDirNumberAndOffset() ---*/
 
@@ -5505,13 +5600,6 @@
 {
     if (diroff == 0) /* no more directories */
         return 0;
-    if (tif->tif_dirnumber >= TIFF_MAX_DIR_COUNT)
-    {
-        TIFFErrorExtR(tif, "_TIFFGetDirNumberFromOffset",
-                      "Cannot handle more than %u TIFF directories",
-                      TIFF_MAX_DIR_COUNT);
-        return 0;
-    }
 
     /* Check if offset is already in the list and return matching directory
      * number. Otherwise update IFD list using TIFFNumberOfDirectories() and
@@ -5532,6 +5620,7 @@
         return 1;
     }
 
+    /* This updates the directory list for all main-IFDs in the file. */
     TIFFNumberOfDirectories(tif);
 
     foundEntry = (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
@@ -5546,6 +5635,83 @@
 } /*--- _TIFFGetDirNumberFromOffset() ---*/
 
 /*
+ * Retrieve the matching IFD directory offset of a given IFD number
+ * from the list of directories already seen.
+ * Returns 1 if the offset was in the list of already seen IFDs and the
+ * directory offset can be returned. The directory list is not updated.
+ * Otherwise returns 0 or if an error occurred.
+ */
+int _TIFFGetOffsetFromDirNumber(TIFF *tif, tdir_t dirn, uint64_t *diroff)
+{
+
+    if (tif->tif_map_dir_number_to_offset == NULL)
+        return 0;
+    TIFFOffsetAndDirNumber entry;
+    entry.offset = 0; /* not used */
+    entry.dirNumber = dirn;
+
+    TIFFOffsetAndDirNumber *foundEntry =
+        (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+            tif->tif_map_dir_number_to_offset, &entry);
+    if (foundEntry)
+    {
+        *diroff = foundEntry->offset;
+        return 1;
+    }
+
+    return 0;
+} /*--- _TIFFGetOffsetFromDirNumber() ---*/
+
+/*
+ * Remove an entry from the directory list of already seen directories
+ * by directory offset.
+ * If an entry is to be removed from the list, it is also okay if the entry
+ * is not in the list or the list does not exist.
+ */
+int _TIFFRemoveEntryFromDirectoryListByOffset(TIFF *tif, uint64_t diroff)
+{
+    if (tif->tif_map_dir_offset_to_number == NULL)
+        return 1;
+
+    TIFFOffsetAndDirNumber entryOld;
+    entryOld.offset = diroff;
+    entryOld.dirNumber = 0;
+    /* We must remove first from tif_map_dir_number_to_offset as the
+     * entry is owned (and thus freed) by tif_map_dir_offset_to_number.
+     * However, we need firstly to find the directory number from offset. */
+
+    TIFFOffsetAndDirNumber *foundEntryOldOff =
+        (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+            tif->tif_map_dir_offset_to_number, &entryOld);
+    if (foundEntryOldOff)
+    {
+        entryOld.dirNumber = foundEntryOldOff->dirNumber;
+        if (tif->tif_map_dir_number_to_offset != NULL)
+        {
+            TIFFOffsetAndDirNumber *foundEntryOldDir =
+                (TIFFOffsetAndDirNumber *)TIFFHashSetLookup(
+                    tif->tif_map_dir_number_to_offset, &entryOld);
+            if (foundEntryOldDir)
+            {
+                TIFFHashSetRemove(tif->tif_map_dir_number_to_offset,
+                                  foundEntryOldDir);
+                TIFFHashSetRemove(tif->tif_map_dir_offset_to_number,
+                                  foundEntryOldOff);
+                return 1;
+            }
+        }
+        else
+        {
+            TIFFErrorExtR(tif, "_TIFFRemoveEntryFromDirectoryListByOffset",
+                          "Unexpectedly tif_map_dir_number_to_offset is "
+                          "missing but tif_map_dir_offset_to_number exists.");
+            return 0;
+        }
+    }
+    return 1;
+} /*--- _TIFFRemoveEntryFromDirectoryListByOffset() ---*/
+
+/*
  * Check the count field of a directory entry against a known value.  The
  * caller is expected to skip/ignore the tag if there is a mismatch.
  */
diff --git a/third_party/libtiff/tif_dirwrite.c b/third_party/libtiff/tif_dirwrite.c
index beebd11..a6a485f 100644
--- a/third_party/libtiff/tif_dirwrite.c
+++ b/third_party/libtiff/tif_dirwrite.c
@@ -320,6 +320,7 @@
      * Find and zero the pointer to this directory, so that TIFFLinkDirectory
      * will cause it to be added after this directories current pre-link.
      */
+    uint64_t torewritediroff = tif->tif_diroff;
 
     if (!(tif->tif_flags & TIFF_BIGTIFF))
     {
@@ -387,6 +388,8 @@
                 nextdir = nextnextdir;
             }
         }
+        /* Remove skipped offset from IFD loop directory list. */
+        _TIFFRemoveEntryFromDirectoryListByOffset(tif, torewritediroff);
     }
     else
     {
@@ -456,6 +459,8 @@
                 nextdir = nextnextdir;
             }
         }
+        /* Remove skipped offset from IFD loop directory list. */
+        _TIFFRemoveEntryFromDirectoryListByOffset(tif, torewritediroff);
     }
 
     /*
@@ -1114,7 +1119,12 @@
         if (tif->tif_dataoff & 1)
             tif->tif_dataoff++;
         if (isimage)
-            tif->tif_curdir++;
+        {
+            if (tif->tif_curdir == TIFF_NON_EXISTENT_DIR_NUMBER)
+                tif->tif_curdir = 0;
+            else
+                tif->tif_curdir++;
+        }
     }
     if (isimage)
     {
diff --git a/third_party/libtiff/tif_fax3.c b/third_party/libtiff/tif_fax3.c
index 9d66fc0..a3c645c 100644
--- a/third_party/libtiff/tif_fax3.c
+++ b/third_party/libtiff/tif_fax3.c
@@ -537,7 +537,7 @@
       TIFFroundup and TIFFSafeMultiply return zero on integer overflow
     */
     dsp->runs = (uint32_t *)NULL;
-    dsp->nruns = TIFFroundup_32(rowpixels, 32);
+    dsp->nruns = TIFFroundup_32(rowpixels + 1, 32);
     if (needsRefLine)
     {
         dsp->nruns = TIFFSafeMultiply(uint32_t, dsp->nruns, 2);
diff --git a/third_party/libtiff/tif_hash_set.c b/third_party/libtiff/tif_hash_set.c
index 49718ce..cec22ab 100644
--- a/third_party/libtiff/tif_hash_set.c
+++ b/third_party/libtiff/tif_hash_set.c
@@ -26,6 +26,8 @@
  * DEALINGS IN THE SOFTWARE.
  ****************************************************************************/
 
+#include "tiffconf.h"
+
 #include "tif_hash_set.h"
 
 #include <assert.h>
@@ -161,7 +163,6 @@
     return set;
 }
 
-#ifdef notdef
 /************************************************************************/
 /*                          TIFFHashSetSize()                            */
 /************************************************************************/
@@ -181,7 +182,6 @@
     assert(set != NULL);
     return set->nSize;
 }
-#endif
 
 /************************************************************************/
 /*                       TIFFHashSetGetNewListElt()                      */
diff --git a/third_party/libtiff/tif_hash_set.h b/third_party/libtiff/tif_hash_set.h
index 1a3f8da..f60e2c6 100644
--- a/third_party/libtiff/tif_hash_set.h
+++ b/third_party/libtiff/tif_hash_set.h
@@ -71,11 +71,11 @@
 
     void TIFFHashSetDestroy(TIFFHashSet *set);
 
+    int TIFFHashSetSize(const TIFFHashSet *set);
+
 #ifdef notused
     void TIFFHashSetClear(TIFFHashSet *set);
 
-    int TIFFHashSetSize(const TIFFHashSet *set);
-
     /** TIFFHashSetIterEltFunc */
     typedef int (*TIFFHashSetIterEltFunc)(void *elt, void *user_data);
 
diff --git a/third_party/libtiff/tif_luv.c b/third_party/libtiff/tif_luv.c
index 051721e..021756d 100644
--- a/third_party/libtiff/tif_luv.c
+++ b/third_party/libtiff/tif_luv.c
@@ -953,6 +953,13 @@
 {
     register int vi, ui;
 
+    /* check for NaN */
+    if (u != u || v != v)
+    {
+        u = U_NEU;
+        v = V_NEU;
+    }
+
     if (v < UV_VSTART)
         return oog_encode(u, v);
     vi = tiff_itrunc((v - UV_VSTART) * (1. / UV_SQSIZ), em);
diff --git a/third_party/libtiff/tif_lzw.c b/third_party/libtiff/tif_lzw.c
index ba75a07..d631fa1 100644
--- a/third_party/libtiff/tif_lzw.c
+++ b/third_party/libtiff/tif_lzw.c
@@ -423,6 +423,10 @@
 
     if (sp->read_error)
     {
+        TIFFErrorExtR(tif, module,
+                      "LZWDecode: Scanline %" PRIu32 " cannot be read due to "
+                      "previous error",
+                      tif->tif_row);
         return 0;
     }
 
@@ -742,6 +746,7 @@
     return (1);
 
 no_eoi:
+    sp->read_error = 1;
     TIFFErrorExtR(tif, module,
                   "LZWDecode: Strip %" PRIu32 " not terminated with EOI code",
                   tif->tif_curstrip);
diff --git a/third_party/libtiff/tif_open.c b/third_party/libtiff/tif_open.c
index 8a86a26..23fcf81 100644
--- a/third_party/libtiff/tif_open.c
+++ b/third_party/libtiff/tif_open.c
@@ -365,8 +365,12 @@
                     (tif->tif_flags & ~TIFF_FILLORDER) | FILLORDER_LSB2MSB;
                 break;
             case 'H':
+                TIFFWarningExtR(tif, name,
+                                "H(ost) mode is deprecated. Since "
+                                "libtiff 4.5.1, it is an alias of 'B' / "
+                                "FILLORDER_MSB2LSB.");
                 tif->tif_flags =
-                    (tif->tif_flags & ~TIFF_FILLORDER) | HOST_FILLORDER;
+                    (tif->tif_flags & ~TIFF_FILLORDER) | FILLORDER_MSB2LSB;
                 break;
             case 'M':
                 if (m == O_RDONLY)
@@ -485,7 +489,7 @@
             goto bad;
         tif->tif_diroff = 0;
         tif->tif_lastdiroff = 0;
-        tif->tif_dirnumber = 0;
+        tif->tif_setdirectory_force_absolute = FALSE;
         return (tif);
     }
     /*
diff --git a/third_party/libtiff/tiff.h b/third_party/libtiff/tiff.h
index 285a968..b2d1186 100644
--- a/third_party/libtiff/tiff.h
+++ b/third_party/libtiff/tiff.h
@@ -166,30 +166,30 @@
 /*
  * TIFF Tag Definitions.
  */
-#define TIFFTAG_SUBFILETYPE 254   /* subfile data descriptor */
-#define FILETYPE_REDUCEDIMAGE 0x1 /* reduced resolution version */
-#define FILETYPE_PAGE 0x2         /* one page of many */
-#define FILETYPE_MASK 0x4         /* transparency mask */
-#define TIFFTAG_OSUBFILETYPE 255  /* +kind of data in subfile */
-#define OFILETYPE_IMAGE 1         /* full resolution image data */
-#define OFILETYPE_REDUCEDIMAGE 2  /* reduced size image data */
-#define OFILETYPE_PAGE 3          /* one page of many */
-#define TIFFTAG_IMAGEWIDTH 256    /* image width in pixels */
-#define TIFFTAG_IMAGELENGTH 257   /* image height in pixels */
-#define TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */
-#define TIFFTAG_COMPRESSION 259   /* data compression technique */
-#define COMPRESSION_NONE 1        /* dump mode */
-#define COMPRESSION_CCITTRLE 2    /* CCITT modified Huffman RLE */
-#define COMPRESSION_CCITTFAX3 3   /* CCITT Group 3 fax encoding */
-#define COMPRESSION_CCITT_T4 3    /* CCITT T.4 (TIFF 6 name) */
-#define COMPRESSION_CCITTFAX4 4   /* CCITT Group 4 fax encoding */
-#define COMPRESSION_CCITT_T6 4    /* CCITT T.6 (TIFF 6 name) */
-#define COMPRESSION_LZW 5         /* Lempel-Ziv  & Welch */
-#define COMPRESSION_OJPEG 6       /* !6.0 JPEG */
-#define COMPRESSION_JPEG 7        /* %JPEG DCT compression */
-#define COMPRESSION_T85 9         /* !TIFF/FX T.85 JBIG compression */
-#define COMPRESSION_T43                                                        \
-    10 /* !TIFF/FX T.43 colour by layered JBIG compression */
+/* clang-format off */   /* for better readability of tag comments */
+#define TIFFTAG_SUBFILETYPE 254       /* subfile data descriptor */
+#define FILETYPE_REDUCEDIMAGE 0x1     /* reduced resolution version */
+#define FILETYPE_PAGE 0x2             /* one page of many */
+#define FILETYPE_MASK 0x4             /* transparency mask */
+#define TIFFTAG_OSUBFILETYPE 255      /* +kind of data in subfile */
+#define OFILETYPE_IMAGE 1             /* full resolution image data */
+#define OFILETYPE_REDUCEDIMAGE 2      /* reduced size image data */
+#define OFILETYPE_PAGE 3              /* one page of many */
+#define TIFFTAG_IMAGEWIDTH 256        /* image width in pixels */
+#define TIFFTAG_IMAGELENGTH 257       /* image height in pixels */
+#define TIFFTAG_BITSPERSAMPLE 258     /* bits per channel (sample) */
+#define TIFFTAG_COMPRESSION 259       /* data compression technique */
+#define COMPRESSION_NONE 1            /* dump mode */
+#define COMPRESSION_CCITTRLE 2        /* CCITT modified Huffman RLE */
+#define COMPRESSION_CCITTFAX3 3       /* CCITT Group 3 fax encoding */
+#define COMPRESSION_CCITT_T4 3        /* CCITT T.4 (TIFF 6 name) */
+#define COMPRESSION_CCITTFAX4 4       /* CCITT Group 4 fax encoding */
+#define COMPRESSION_CCITT_T6 4        /* CCITT T.6 (TIFF 6 name) */
+#define COMPRESSION_LZW 5             /* Lempel-Ziv  & Welch */
+#define COMPRESSION_OJPEG 6           /* !6.0 JPEG */
+#define COMPRESSION_JPEG 7            /* %JPEG DCT compression */
+#define COMPRESSION_T85 9             /* !TIFF/FX T.85 JBIG compression */
+#define COMPRESSION_T43 10            /* !TIFF/FX T.43 colour by layered JBIG compression */
 #define COMPRESSION_NEXT 32766        /* NeXT 2-bit RLE */
 #define COMPRESSION_CCITTRLEW 32771   /* #1 w/ word alignment */
 #define COMPRESSION_PACKBITS 32773    /* Macintosh RLE */
@@ -203,113 +203,107 @@
 #define COMPRESSION_PIXARFILM 32908 /* Pixar companded 10bit LZW */
 #define COMPRESSION_PIXARLOG 32909  /* Pixar companded 11bit ZIP */
 #define COMPRESSION_DEFLATE 32946   /* Deflate compression, legacy tag */
-#define COMPRESSION_ADOBE_DEFLATE                                              \
-    8 /* Deflate compression,                                                  \
-         as recognized by Adobe */
+#define COMPRESSION_ADOBE_DEFLATE 8 /* Deflate compression, as recognized by Adobe */
 /* compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> */
 #define COMPRESSION_DCS 32947      /* Kodak DCS encoding */
 #define COMPRESSION_JBIG 34661     /* ISO JBIG */
 #define COMPRESSION_SGILOG 34676   /* SGI Log Luminance RLE */
 #define COMPRESSION_SGILOG24 34677 /* SGI Log 24-bit packed */
 #define COMPRESSION_JP2000 34712   /* Leadtools JPEG2000 */
-#define COMPRESSION_LERC                                                       \
-    34887 /* ESRI Lerc codec: https://github.com/Esri/lerc */
+#define COMPRESSION_LERC 34887     /* ESRI Lerc codec: https://github.com/Esri/lerc */
 /* compression codes 34887-34889 are reserved for ESRI */
-#define COMPRESSION_LZMA 34925 /* LZMA2 */
-#define COMPRESSION_ZSTD                                                       \
-    50000 /* ZSTD: WARNING not registered in Adobe-maintained registry */
-#define COMPRESSION_WEBP                                                       \
-    50001 /* WEBP: WARNING not registered in Adobe-maintained registry */
-#define COMPRESSION_JXL                                                        \
-    50002 /* JPEGXL: WARNING not registered in Adobe-maintained registry */
-#define TIFFTAG_PHOTOMETRIC 262       /* photometric interpretation */
-#define PHOTOMETRIC_MINISWHITE 0      /* min value is white */
-#define PHOTOMETRIC_MINISBLACK 1      /* min value is black */
-#define PHOTOMETRIC_RGB 2             /* RGB color model */
-#define PHOTOMETRIC_PALETTE 3         /* color map indexed */
-#define PHOTOMETRIC_MASK 4            /* $holdout mask */
-#define PHOTOMETRIC_SEPARATED 5       /* !color separations */
-#define PHOTOMETRIC_YCBCR 6           /* !CCIR 601 */
-#define PHOTOMETRIC_CIELAB 8          /* !1976 CIE L*a*b* */
-#define PHOTOMETRIC_ICCLAB 9          /* ICC L*a*b* [Adobe TIFF Technote 4] */
-#define PHOTOMETRIC_ITULAB 10         /* ITU L*a*b* */
-#define PHOTOMETRIC_CFA 32803         /* color filter array */
-#define PHOTOMETRIC_LOGL 32844        /* CIE Log2(L) */
-#define PHOTOMETRIC_LOGLUV 32845      /* CIE Log2(L) (u',v') */
-#define TIFFTAG_THRESHHOLDING 263     /* +thresholding used on data */
-#define THRESHHOLD_BILEVEL 1          /* b&w art scan */
-#define THRESHHOLD_HALFTONE 2         /* or dithered scan */
-#define THRESHHOLD_ERRORDIFFUSE 3     /* usually floyd-steinberg */
-#define TIFFTAG_CELLWIDTH 264         /* +dithering matrix width */
-#define TIFFTAG_CELLLENGTH 265        /* +dithering matrix height */
-#define TIFFTAG_FILLORDER 266         /* data order within a byte */
-#define FILLORDER_MSB2LSB 1           /* most significant -> least */
-#define FILLORDER_LSB2MSB 2           /* least significant -> most */
-#define TIFFTAG_DOCUMENTNAME 269      /* name of doc. image is from */
-#define TIFFTAG_IMAGEDESCRIPTION 270  /* info about image */
-#define TIFFTAG_MAKE 271              /* scanner manufacturer name */
-#define TIFFTAG_MODEL 272             /* scanner model name/number */
-#define TIFFTAG_STRIPOFFSETS 273      /* offsets to data strips */
-#define TIFFTAG_ORIENTATION 274       /* +image orientation */
-#define ORIENTATION_TOPLEFT 1         /* row 0 top, col 0 lhs */
-#define ORIENTATION_TOPRIGHT 2        /* row 0 top, col 0 rhs */
-#define ORIENTATION_BOTRIGHT 3        /* row 0 bottom, col 0 rhs */
-#define ORIENTATION_BOTLEFT 4         /* row 0 bottom, col 0 lhs */
-#define ORIENTATION_LEFTTOP 5         /* row 0 lhs, col 0 top */
-#define ORIENTATION_RIGHTTOP 6        /* row 0 rhs, col 0 top */
-#define ORIENTATION_RIGHTBOT 7        /* row 0 rhs, col 0 bottom */
-#define ORIENTATION_LEFTBOT 8         /* row 0 lhs, col 0 bottom */
-#define TIFFTAG_SAMPLESPERPIXEL 277   /* samples per pixel */
-#define TIFFTAG_ROWSPERSTRIP 278      /* rows per strip of data */
-#define TIFFTAG_STRIPBYTECOUNTS 279   /* bytes counts for strips */
-#define TIFFTAG_MINSAMPLEVALUE 280    /* +minimum sample value */
-#define TIFFTAG_MAXSAMPLEVALUE 281    /* +maximum sample value */
-#define TIFFTAG_XRESOLUTION 282       /* pixels/resolution in x */
-#define TIFFTAG_YRESOLUTION 283       /* pixels/resolution in y */
-#define TIFFTAG_PLANARCONFIG 284      /* storage organization */
-#define PLANARCONFIG_CONTIG 1         /* single image plane */
-#define PLANARCONFIG_SEPARATE 2       /* separate planes of data */
-#define TIFFTAG_PAGENAME 285          /* page name image is from */
-#define TIFFTAG_XPOSITION 286         /* x page offset of image lhs */
-#define TIFFTAG_YPOSITION 287         /* y page offset of image lhs */
-#define TIFFTAG_FREEOFFSETS 288       /* +byte offset to free block */
-#define TIFFTAG_FREEBYTECOUNTS 289    /* +sizes of free blocks */
-#define TIFFTAG_GRAYRESPONSEUNIT 290  /* $gray scale curve accuracy */
-#define GRAYRESPONSEUNIT_10S 1        /* tenths of a unit */
-#define GRAYRESPONSEUNIT_100S 2       /* hundredths of a unit */
-#define GRAYRESPONSEUNIT_1000S 3      /* thousandths of a unit */
-#define GRAYRESPONSEUNIT_10000S 4     /* ten-thousandths of a unit */
-#define GRAYRESPONSEUNIT_100000S 5    /* hundred-thousandths */
-#define TIFFTAG_GRAYRESPONSECURVE 291 /* $gray scale response curve */
-#define TIFFTAG_GROUP3OPTIONS 292     /* 32 flag bits */
-#define TIFFTAG_T4OPTIONS 292         /* TIFF 6.0 proper name alias */
-#define GROUP3OPT_2DENCODING 0x1      /* 2-dimensional coding */
-#define GROUP3OPT_UNCOMPRESSED 0x2    /* data not compressed */
-#define GROUP3OPT_FILLBITS 0x4        /* fill to byte boundary */
-#define TIFFTAG_GROUP4OPTIONS 293     /* 32 flag bits */
-#define TIFFTAG_T6OPTIONS 293         /* TIFF 6.0 proper name */
-#define GROUP4OPT_UNCOMPRESSED 0x2    /* data not compressed */
-#define TIFFTAG_RESOLUTIONUNIT 296    /* units of resolutions */
-#define RESUNIT_NONE 1                /* no meaningful units */
-#define RESUNIT_INCH 2                /* english */
-#define RESUNIT_CENTIMETER 3          /* metric */
-#define TIFFTAG_PAGENUMBER 297        /* page numbers of multi-page */
-#define TIFFTAG_COLORRESPONSEUNIT 300 /* $color curve accuracy */
-#define COLORRESPONSEUNIT_10S 1       /* tenths of a unit */
-#define COLORRESPONSEUNIT_100S 2      /* hundredths of a unit */
-#define COLORRESPONSEUNIT_1000S 3     /* thousandths of a unit */
-#define COLORRESPONSEUNIT_10000S 4    /* ten-thousandths of a unit */
-#define COLORRESPONSEUNIT_100000S 5   /* hundred-thousandths */
-#define TIFFTAG_TRANSFERFUNCTION 301  /* !colorimetry info */
-#define TIFFTAG_SOFTWARE 305          /* name & release */
-#define TIFFTAG_DATETIME 306          /* creation date and time */
-#define TIFFTAG_ARTIST 315            /* creator of image */
-#define TIFFTAG_HOSTCOMPUTER 316      /* machine where created */
-#define TIFFTAG_PREDICTOR 317         /* prediction scheme w/ LZW */
-#define PREDICTOR_NONE 1              /* no prediction scheme used */
-#define PREDICTOR_HORIZONTAL 2        /* horizontal differencing */
-#define PREDICTOR_FLOATINGPOINT 3     /* floating point predictor */
-#define TIFFTAG_WHITEPOINT 318        /* image white point */
+#define COMPRESSION_LZMA 34925             /* LZMA2 */
+#define COMPRESSION_ZSTD 50000             /* ZSTD: WARNING not registered in Adobe-maintained registry */
+#define COMPRESSION_WEBP 50001             /* WEBP: WARNING not registered in Adobe-maintained registry */
+#define COMPRESSION_JXL 50002              /* JPEGXL: WARNING not registered in Adobe-maintained registry */
+#define TIFFTAG_PHOTOMETRIC 262            /* photometric interpretation */
+#define PHOTOMETRIC_MINISWHITE 0           /* min value is white */
+#define PHOTOMETRIC_MINISBLACK 1           /* min value is black */
+#define PHOTOMETRIC_RGB 2                  /* RGB color model */
+#define PHOTOMETRIC_PALETTE 3              /* color map indexed */
+#define PHOTOMETRIC_MASK 4                 /* $holdout mask */
+#define PHOTOMETRIC_SEPARATED 5            /* !color separations */
+#define PHOTOMETRIC_YCBCR 6                /* !CCIR 601 */
+#define PHOTOMETRIC_CIELAB 8               /* !1976 CIE L*a*b* */
+#define PHOTOMETRIC_ICCLAB 9               /* ICC L*a*b* [Adobe TIFF Technote 4] */
+#define PHOTOMETRIC_ITULAB 10              /* ITU L*a*b* */
+#define PHOTOMETRIC_CFA 32803              /* color filter array */
+#define PHOTOMETRIC_LOGL 32844             /* CIE Log2(L) */
+#define PHOTOMETRIC_LOGLUV 32845           /* CIE Log2(L) (u',v') */
+#define TIFFTAG_THRESHHOLDING 263          /* +thresholding used on data */
+#define THRESHHOLD_BILEVEL 1               /* b&w art scan */
+#define THRESHHOLD_HALFTONE 2              /* or dithered scan */
+#define THRESHHOLD_ERRORDIFFUSE 3          /* usually floyd-steinberg */
+#define TIFFTAG_CELLWIDTH 264              /* +dithering matrix width */
+#define TIFFTAG_CELLLENGTH 265             /* +dithering matrix height */
+#define TIFFTAG_FILLORDER 266              /* data order within a byte */
+#define FILLORDER_MSB2LSB 1                /* most significant -> least */
+#define FILLORDER_LSB2MSB 2                /* least significant -> most */
+#define TIFFTAG_DOCUMENTNAME 269           /* name of doc. image is from */
+#define TIFFTAG_IMAGEDESCRIPTION 270       /* info about image */
+#define TIFFTAG_MAKE 271                   /* scanner manufacturer name */
+#define TIFFTAG_MODEL 272                  /* scanner model name/number */
+#define TIFFTAG_STRIPOFFSETS 273           /* offsets to data strips */
+#define TIFFTAG_ORIENTATION 274            /* +image orientation */
+#define ORIENTATION_TOPLEFT 1              /* row 0 top, col 0 lhs */
+#define ORIENTATION_TOPRIGHT 2             /* row 0 top, col 0 rhs */
+#define ORIENTATION_BOTRIGHT 3             /* row 0 bottom, col 0 rhs */
+#define ORIENTATION_BOTLEFT 4              /* row 0 bottom, col 0 lhs */
+#define ORIENTATION_LEFTTOP 5              /* row 0 lhs, col 0 top */
+#define ORIENTATION_RIGHTTOP 6             /* row 0 rhs, col 0 top */
+#define ORIENTATION_RIGHTBOT 7             /* row 0 rhs, col 0 bottom */
+#define ORIENTATION_LEFTBOT 8              /* row 0 lhs, col 0 bottom */
+#define TIFFTAG_SAMPLESPERPIXEL 277        /* samples per pixel */
+#define TIFFTAG_ROWSPERSTRIP 278           /* rows per strip of data */
+#define TIFFTAG_STRIPBYTECOUNTS 279        /* bytes counts for strips */
+#define TIFFTAG_MINSAMPLEVALUE 280         /* +minimum sample value */
+#define TIFFTAG_MAXSAMPLEVALUE 281         /* +maximum sample value */
+#define TIFFTAG_XRESOLUTION 282            /* pixels/resolution in x */
+#define TIFFTAG_YRESOLUTION 283            /* pixels/resolution in y */
+#define TIFFTAG_PLANARCONFIG 284           /* storage organization */
+#define PLANARCONFIG_CONTIG 1              /* single image plane */
+#define PLANARCONFIG_SEPARATE 2            /* separate planes of data */
+#define TIFFTAG_PAGENAME 285               /* page name image is from */
+#define TIFFTAG_XPOSITION 286              /* x page offset of image lhs */
+#define TIFFTAG_YPOSITION 287              /* y page offset of image lhs */
+#define TIFFTAG_FREEOFFSETS 288            /* +byte offset to free block */
+#define TIFFTAG_FREEBYTECOUNTS 289         /* +sizes of free blocks */
+#define TIFFTAG_GRAYRESPONSEUNIT 290       /* $gray scale curve accuracy */
+#define GRAYRESPONSEUNIT_10S 1             /* tenths of a unit */
+#define GRAYRESPONSEUNIT_100S 2            /* hundredths of a unit */
+#define GRAYRESPONSEUNIT_1000S 3           /* thousandths of a unit */
+#define GRAYRESPONSEUNIT_10000S 4          /* ten-thousandths of a unit */
+#define GRAYRESPONSEUNIT_100000S 5         /* hundred-thousandths */
+#define TIFFTAG_GRAYRESPONSECURVE 291      /* $gray scale response curve */
+#define TIFFTAG_GROUP3OPTIONS 292          /* 32 flag bits */
+#define TIFFTAG_T4OPTIONS 292              /* TIFF 6.0 proper name alias */
+#define GROUP3OPT_2DENCODING 0x1           /* 2-dimensional coding */
+#define GROUP3OPT_UNCOMPRESSED 0x2         /* data not compressed */
+#define GROUP3OPT_FILLBITS 0x4             /* fill to byte boundary */
+#define TIFFTAG_GROUP4OPTIONS 293          /* 32 flag bits */
+#define TIFFTAG_T6OPTIONS 293              /* TIFF 6.0 proper name */
+#define GROUP4OPT_UNCOMPRESSED 0x2         /* data not compressed */
+#define TIFFTAG_RESOLUTIONUNIT 296         /* units of resolutions */
+#define RESUNIT_NONE 1                     /* no meaningful units */
+#define RESUNIT_INCH 2                     /* english */
+#define RESUNIT_CENTIMETER 3               /* metric */
+#define TIFFTAG_PAGENUMBER 297             /* page numbers of multi-page */
+#define TIFFTAG_COLORRESPONSEUNIT 300      /* $color curve accuracy */
+#define COLORRESPONSEUNIT_10S 1            /* tenths of a unit */
+#define COLORRESPONSEUNIT_100S 2           /* hundredths of a unit */
+#define COLORRESPONSEUNIT_1000S 3          /* thousandths of a unit */
+#define COLORRESPONSEUNIT_10000S 4         /* ten-thousandths of a unit */
+#define COLORRESPONSEUNIT_100000S 5        /* hundred-thousandths */
+#define TIFFTAG_TRANSFERFUNCTION 301       /* !colorimetry info */
+#define TIFFTAG_SOFTWARE 305               /* name & release */
+#define TIFFTAG_DATETIME 306               /* creation date and time */
+#define TIFFTAG_ARTIST 315                 /* creator of image */
+#define TIFFTAG_HOSTCOMPUTER 316           /* machine where created */
+#define TIFFTAG_PREDICTOR 317              /* prediction scheme w/ LZW */
+#define PREDICTOR_NONE 1                   /* no prediction scheme used */
+#define PREDICTOR_HORIZONTAL 2             /* horizontal differencing */
+#define PREDICTOR_FLOATINGPOINT 3          /* floating point predictor */
+#define TIFFTAG_WHITEPOINT 318             /* image white point */
 #define TIFFTAG_PRIMARYCHROMATICITIES 319  /* !primary chromaticities */
 #define TIFFTAG_COLORMAP 320               /* RGB map for palette image */
 #define TIFFTAG_HALFTONEHINTS 321          /* !highlight+shadow info */
@@ -325,39 +319,31 @@
 #define TIFFTAG_CONSECUTIVEBADFAXLINES 328 /* max consecutive bad lines */
 #define TIFFTAG_SUBIFD 330                 /* subimage descriptors */
 #define TIFFTAG_INKSET 332                 /* !inks in separated image */
-#define INKSET_CMYK 1                /* !cyan-magenta-yellow-black color */
-#define INKSET_MULTIINK 2            /* !multi-ink or hi-fi color */
-#define TIFFTAG_INKNAMES 333         /* !ascii names of inks */
-#define TIFFTAG_NUMBEROFINKS 334     /* !number of inks */
-#define TIFFTAG_DOTRANGE 336         /* !0% and 100% dot codes */
-#define TIFFTAG_TARGETPRINTER 337    /* !separation target */
-#define TIFFTAG_EXTRASAMPLES 338     /* !info about extra samples */
-#define EXTRASAMPLE_UNSPECIFIED 0    /* !unspecified data */
-#define EXTRASAMPLE_ASSOCALPHA 1     /* !associated alpha data */
-#define EXTRASAMPLE_UNASSALPHA 2     /* !unassociated alpha data */
-#define TIFFTAG_SAMPLEFORMAT 339     /* !data sample format */
-#define SAMPLEFORMAT_UINT 1          /* !unsigned integer data */
-#define SAMPLEFORMAT_INT 2           /* !signed integer data */
-#define SAMPLEFORMAT_IEEEFP 3        /* !IEEE floating point data */
-#define SAMPLEFORMAT_VOID 4          /* !untyped data */
-#define SAMPLEFORMAT_COMPLEXINT 5    /* !complex signed int */
-#define SAMPLEFORMAT_COMPLEXIEEEFP 6 /* !complex ieee floating */
-#define TIFFTAG_SMINSAMPLEVALUE 340  /* !variable MinSampleValue */
-#define TIFFTAG_SMAXSAMPLEVALUE 341  /* !variable MaxSampleValue */
-#define TIFFTAG_CLIPPATH                                                       \
-    343 /* %ClipPath                                                           \
-           [Adobe TIFF technote 2] */
-#define TIFFTAG_XCLIPPATHUNITS                                                 \
-    344 /* %XClipPathUnits                                                     \
-           [Adobe TIFF technote 2] */
-#define TIFFTAG_YCLIPPATHUNITS                                                 \
-    345 /* %YClipPathUnits                                                     \
-           [Adobe TIFF technote 2] */
-#define TIFFTAG_INDEXED                                                        \
-    346                        /* %Indexed                                     \
-                                  [Adobe TIFF Technote 3] */
-#define TIFFTAG_JPEGTABLES 347 /* %JPEG table stream */
-#define TIFFTAG_OPIPROXY 351   /* %OPI Proxy [Adobe TIFF technote] */
+#define INKSET_CMYK 1                      /* !cyan-magenta-yellow-black color */
+#define INKSET_MULTIINK 2                  /* !multi-ink or hi-fi color */
+#define TIFFTAG_INKNAMES 333               /* !ascii names of inks */
+#define TIFFTAG_NUMBEROFINKS 334           /* !number of inks */
+#define TIFFTAG_DOTRANGE 336               /* !0% and 100% dot codes */
+#define TIFFTAG_TARGETPRINTER 337          /* !separation target */
+#define TIFFTAG_EXTRASAMPLES 338           /* !info about extra samples */
+#define EXTRASAMPLE_UNSPECIFIED 0          /* !unspecified data */
+#define EXTRASAMPLE_ASSOCALPHA 1           /* !associated alpha data */
+#define EXTRASAMPLE_UNASSALPHA 2           /* !unassociated alpha data */
+#define TIFFTAG_SAMPLEFORMAT 339           /* !data sample format */
+#define SAMPLEFORMAT_UINT 1                /* !unsigned integer data */
+#define SAMPLEFORMAT_INT 2                 /* !signed integer data */
+#define SAMPLEFORMAT_IEEEFP 3              /* !IEEE floating point data */
+#define SAMPLEFORMAT_VOID 4                /* !untyped data */
+#define SAMPLEFORMAT_COMPLEXINT 5          /* !complex signed int */
+#define SAMPLEFORMAT_COMPLEXIEEEFP 6       /* !complex ieee floating */
+#define TIFFTAG_SMINSAMPLEVALUE 340        /* !variable MinSampleValue */
+#define TIFFTAG_SMAXSAMPLEVALUE 341        /* !variable MaxSampleValue */
+#define TIFFTAG_CLIPPATH 343               /* %ClipPath [Adobe TIFF technote 2] */
+#define TIFFTAG_XCLIPPATHUNITS 344         /* %XClipPathUnits [Adobe TIFF technote 2] */
+#define TIFFTAG_YCLIPPATHUNITS 345         /* %YClipPathUnits [Adobe TIFF technote 2] */
+#define TIFFTAG_INDEXED 346                /* %Indexed [Adobe TIFF Technote 3] */
+#define TIFFTAG_JPEGTABLES 347             /* %JPEG table stream */
+#define TIFFTAG_OPIPROXY 351               /* %OPI Proxy [Adobe TIFF technote] */
 /* Tags 400-435 are from the TIFF/FX spec */
 #define TIFFTAG_GLOBALPARAMETERSIFD 400 /* ! */
 #define TIFFTAG_PROFILETYPE 401         /* ! */
@@ -404,16 +390,11 @@
 #define YCBCRPOSITION_COSITED 2            /* !as in CCIR 601-1 */
 #define TIFFTAG_REFERENCEBLACKWHITE 532    /* !colorimetry info */
 #define TIFFTAG_STRIPROWCOUNTS 559         /* !TIFF/FX strip row counts */
-#define TIFFTAG_XMLPACKET                                                      \
-    700 /* %XML packet                                                         \
-           [Adobe XMP Specification,                                           \
-           January 2004 */
-#define TIFFTAG_OPIIMAGEID                                                     \
-    32781 /* %OPI ImageID                                                      \
-             [Adobe TIFF technote] */
-#define TIFFTAG_TIFFANNOTATIONDATA                                                                           \
-    32932 /* http://web.archive.org/web/20050309141348/http://www.kofile.com/support%20pro/faqs/annospec.htm \
-           */
+#define TIFFTAG_XMLPACKET 700              /* %XML packet [Adobe XMP Specification, January 2004 */
+#define TIFFTAG_OPIIMAGEID 32781           /* %OPI ImageID [Adobe TIFF technote] */
+/* For eiStream Annotation Specification, Version 1.00.06 see
+ * http://web.archive.org/web/20050309141348/http://www.kofile.com/support%20pro/faqs/annospec.htm */
+#define TIFFTAG_TIFFANNOTATIONDATA 32932
 /* tags 32952-32956 are private tags registered to Island Graphics */
 #define TIFFTAG_REFPTS 32953            /* image reference points */
 #define TIFFTAG_REGIONTACKPOINT 32954   /* region-xform tack point */
@@ -444,53 +425,36 @@
 #define TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA 33306
 /* tag 33405 is a private tag registered to Eastman Kodak */
 #define TIFFTAG_WRITERSERIALNUMBER 33405  /* device serial number */
-#define TIFFTAG_CFAREPEATPATTERNDIM 33421 /* dimensions of CFA pattern */
-#define TIFFTAG_CFAPATTERN 33422          /* color filter array pattern */
+#define TIFFTAG_CFAREPEATPATTERNDIM 33421 /* (alias for TIFFTAG_EP_CFAREPEATPATTERNDIM)*/
+#define TIFFTAG_CFAPATTERN 33422          /* (alias for TIFFTAG_EP_CFAPATTERN) */
+#define TIFFTAG_BATTERYLEVEL 33423        /* (alias for TIFFTAG_EP_BATTERYLEVEL) */
 /* tag 33432 is listed in the 6.0 spec w/ unknown ownership */
 #define TIFFTAG_COPYRIGHT 33432 /* copyright string */
-/* Tags 33445-33452 are used for GEL fileformat, see
- * http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf
+/* Tags 33445-33452 are used for Molecular Dynamics GEL fileformat,
+ * see http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf
+ * (2023: the above web site is unavailable but tags are explained briefly at
+ * https://www.awaresystems.be/imaging/tiff/tifftags/docs/gel.html
  */
-#define TIFFTAG_MD_FILETAG                                                                                        \
-    33445 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_SCALEPIXEL                                                                                     \
-    33446 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_COLORTABLE                                                                                     \
-    33447 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_LABNAME                                                                                        \
-    33448 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_SAMPLEINFO                                                                                     \
-    33449 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_PREPDATE                                                                                       \
-    33450 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_PREPTIME                                                                                       \
-    33451 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
-#define TIFFTAG_MD_FILEUNITS                                                                                      \
-    33452 /* http://research.stowers-institute.org/mcm/efg/ScientificSoftware/Utility/TiffTags/GEL-FileFormat.pdf \
-           */
+#define TIFFTAG_MD_FILETAG 33445    /* Specifies the pixel data format encoding in the GEL file format. */
+#define TIFFTAG_MD_SCALEPIXEL 33446 /* scale factor */
+#define TIFFTAG_MD_COLORTABLE 33447 /* conversion from 16bit to 8bit */
+#define TIFFTAG_MD_LABNAME 33448    /* name of the lab that scanned this file. */
+#define TIFFTAG_MD_SAMPLEINFO 33449 /* information about the scanned GEL sample */
+#define TIFFTAG_MD_PREPDATE 33450   /* information about the date the sample was prepared YY/MM/DD */
+#define TIFFTAG_MD_PREPTIME 33451   /* information about the time the sample was prepared HH:MM*/
+#define TIFFTAG_MD_FILEUNITS 33452  /* Units for data in this file, as used in the GEL file format. */
 /* IPTC TAG from RichTIFF specifications */
 #define TIFFTAG_RICHTIFFIPTC 33723
-#define TIFFTAG_INGR_PACKET_DATA_TAG                                           \
-    33918 /* Intergraph Application specific storage. */
-#define TIFFTAG_INGR_FLAG_REGISTERS                                            \
-    33919 /* Intergraph Application specific flags. */
-#define TIFFTAG_IRASB_TRANSORMATION_MATRIX                                     \
-    33920 /* Originally part of Intergraph's GeoTIFF tags, but likely          \
-             understood by IrasB only. */
-#define TIFFTAG_MODELTIEPOINTTAG 33922 /* GeoTIFF */
+#define TIFFTAG_INGR_PACKET_DATA_TAG 33918       /* Intergraph Application specific storage. */
+#define TIFFTAG_INGR_FLAG_REGISTERS 33919        /* Intergraph Application specific flags. */
+#define TIFFTAG_IRASB_TRANSORMATION_MATRIX 33920 /* Originally part of Intergraph's GeoTIFF tags, but likely understood by IrasB only. */
+#define TIFFTAG_MODELTIEPOINTTAG 33922           /* GeoTIFF */
 /* 34016-34029 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
-#define TIFFTAG_IT8SITE 34016             /* site name */
-#define TIFFTAG_IT8COLORSEQUENCE 34017    /* color seq. [RGB,CMYK,etc] */
-#define TIFFTAG_IT8HEADER 34018           /* DDES Header */
-#define TIFFTAG_IT8RASTERPADDING 34019    /* raster scanline padding */
-#define TIFFTAG_IT8BITSPERRUNLENGTH 34020 /* # of bits in short run */
+#define TIFFTAG_IT8SITE 34016                     /* site name */
+#define TIFFTAG_IT8COLORSEQUENCE 34017            /* color seq. [RGB,CMYK,etc] */
+#define TIFFTAG_IT8HEADER 34018                   /* DDES Header */
+#define TIFFTAG_IT8RASTERPADDING 34019            /* raster scanline padding */
+#define TIFFTAG_IT8BITSPERRUNLENGTH 34020         /* # of bits in short run */
 #define TIFFTAG_IT8BITSPEREXTENDEDRUNLENGTH 34021 /* # of bits in long run */
 #define TIFFTAG_IT8COLORTABLE 34022               /* LW colortable */
 #define TIFFTAG_IT8IMAGECOLORINDICATOR 34023      /* BP/BL image color switch */
@@ -501,14 +465,11 @@
 #define TIFFTAG_IT8TRANSPARENCYINDICATOR 34028    /* HC transparency switch */
 #define TIFFTAG_IT8COLORCHARACTERIZATION 34029    /* color character. table */
 #define TIFFTAG_IT8HCUSAGE 34030                  /* HC usage indicator */
-#define TIFFTAG_IT8TRAPINDICATOR                                               \
-    34031                               /* Trapping indicator                  \
-                                           (untrapped=0, trapped=1) */
-#define TIFFTAG_IT8CMYKEQUIVALENT 34032 /* CMYK color equivalents */
+#define TIFFTAG_IT8TRAPINDICATOR 34031            /* Trapping indicator (untrapped=0, trapped=1) */
+#define TIFFTAG_IT8CMYKEQUIVALENT 34032           /* CMYK color equivalents */
 /* tags 34232-34236 are private tags registered to Texas Instruments */
-#define TIFFTAG_FRAMECOUNT 34232 /* Sequence Frame Count */
-#define TIFFTAG_MODELTRANSFORMATIONTAG                                         \
-    34264 /* Used in interchangeable GeoTIFF files */
+#define TIFFTAG_FRAMECOUNT 34232             /* Sequence Frame Count */
+#define TIFFTAG_MODELTRANSFORMATIONTAG 34264 /* Used in interchangeable GeoTIFF files */
 /* tag 34377 is private tag registered to Adobe for PhotoShop */
 #define TIFFTAG_PHOTOSHOP 34377
 /* tags 34665, 34853 and 40965 are documented in EXIF specification */
@@ -518,7 +479,7 @@
 #define TIFFTAG_IMAGELAYER 34732 /* !TIFF/FX image layer information */
 /* tag 34750 is a private tag registered to Pixel Magic */
 #define TIFFTAG_JBIGOPTIONS 34750 /* JBIG options */
-#define TIFFTAG_GPSIFD 34853      /* Pointer to GPS private directory */
+#define TIFFTAG_GPSIFD 34853      /* Pointer to EXIF GPS private directory */
 /* tags 34908-34914 are private tags registered to SGI */
 #define TIFFTAG_FAXRECVPARAMS 34908 /* encoded Class 2 ses. params */
 #define TIFFTAG_FAXSUBADDRESS 34909 /* received SubAddr string */
@@ -527,150 +488,196 @@
 /* tags 37439-37443 are registered to SGI <gregl@sgi.com> */
 #define TIFFTAG_STONITS 37439 /* Sample value to Nits */
 /* tag 34929 is a private tag registered to FedEx */
-#define TIFFTAG_FEDEX_EDR 34929 /* unknown use */
-#define TIFFTAG_IMAGESOURCEDATA                                                \
-    37724 /* http://justsolve.archiveteam.org/wiki/PSD,                        \
-             http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
-#define TIFFTAG_INTEROPERABILITYIFD                                            \
-    40965 /* Pointer to Interoperability private directory */
-#define TIFFTAG_GDAL_METADATA 42112 /* Used by the GDAL library */
-#define TIFFTAG_GDAL_NODATA 42113   /* Used by the GDAL library */
-#define TIFFTAG_OCE_SCANJOB_DESCRIPTION                                        \
-    50215 /* Used in the Oce scanning process */
-#define TIFFTAG_OCE_APPLICATION_SELECTOR                                       \
-    50216 /* Used in the Oce scanning process. */
+#define TIFFTAG_FEDEX_EDR 34929                /* unknown use */
+#define TIFFTAG_IMAGESOURCEDATA 37724          /* http://justsolve.archiveteam.org/wiki/PSD, http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
+#define TIFFTAG_INTEROPERABILITYIFD 40965      /* Pointer to EXIF Interoperability private directory */
+#define TIFFTAG_GDAL_METADATA 42112            /* Used by the GDAL library */
+#define TIFFTAG_GDAL_NODATA 42113              /* Used by the GDAL library */
+#define TIFFTAG_OCE_SCANJOB_DESCRIPTION 50215  /* Used in the Oce scanning process */
+#define TIFFTAG_OCE_APPLICATION_SELECTOR 50216 /* Used in the Oce scanning process. */
 #define TIFFTAG_OCE_IDENTIFICATION_NUMBER 50217
 #define TIFFTAG_OCE_IMAGELOGIC_CHARACTERISTICS 50218
-
 /* tags 50674 to 50677 are reserved for ESRI */
-#define TIFFTAG_LERC_PARAMETERS                                                \
-    50674 /* Stores LERC version and additional compression method */
+#define TIFFTAG_LERC_PARAMETERS 50674 /* Stores LERC version and additional compression method */
+
 /* Adobe Digital Negative (DNG) format tags */
-#define TIFFTAG_DNGVERSION 50706         /* &DNG version number */
-#define TIFFTAG_DNGBACKWARDVERSION 50707 /* &DNG compatibility version */
-#define TIFFTAG_UNIQUECAMERAMODEL 50708  /* &name for the camera model */
-#define TIFFTAG_LOCALIZEDCAMERAMODEL                                           \
-    50709 /* &localized camera model                                           \
-             name */
-#define TIFFTAG_CFAPLANECOLOR                                                  \
-    50710                                /* &CFAPattern->LinearRaw space       \
-                                            mapping */
-#define TIFFTAG_CFALAYOUT 50711          /* &spatial layout of the CFA */
-#define TIFFTAG_LINEARIZATIONTABLE 50712 /* &lookup table description */
-#define TIFFTAG_BLACKLEVELREPEATDIM                                            \
-    50713                        /* &repeat pattern size for                   \
-                                    the BlackLevel tag */
-#define TIFFTAG_BLACKLEVEL 50714 /* &zero light encoding level */
-#define TIFFTAG_BLACKLEVELDELTAH                                               \
-    50715 /* &zero light encoding level                                        \
-             differences (columns) */
-#define TIFFTAG_BLACKLEVELDELTAV                                               \
-    50716 /* &zero light encoding level                                        \
-             differences (rows) */
-#define TIFFTAG_WHITELEVEL                                                     \
-    50717                          /* &fully saturated encoding                \
-                                      level */
-#define TIFFTAG_DEFAULTSCALE 50718 /* &default scale factors */
-#define TIFFTAG_DEFAULTCROPORIGIN                                              \
-    50719 /* &origin of the final image                                        \
-             area */
-#define TIFFTAG_DEFAULTCROPSIZE                                                \
-    50720 /* &size of the final image                                          \
-             area */
-#define TIFFTAG_COLORMATRIX1                                                   \
-    50721 /* &XYZ->reference color space                                       \
-             transformation matrix 1 */
-#define TIFFTAG_COLORMATRIX2                                                   \
-    50722                                /* &XYZ->reference color space        \
-                                            transformation matrix 2 */
-#define TIFFTAG_CAMERACALIBRATION1 50723 /* &calibration matrix 1 */
-#define TIFFTAG_CAMERACALIBRATION2 50724 /* &calibration matrix 2 */
-#define TIFFTAG_REDUCTIONMATRIX1                                               \
-    50725 /* &dimensionality reduction                                         \
-             matrix 1 */
-#define TIFFTAG_REDUCTIONMATRIX2                                               \
-    50726 /* &dimensionality reduction                                         \
-             matrix 2 */
-#define TIFFTAG_ANALOGBALANCE                                                  \
-    50727 /* &gain applied the stored raw                                      \
-             values*/
-#define TIFFTAG_ASSHOTNEUTRAL                                                  \
-    50728 /* &selected white balance in                                        \
-             linear reference space */
-#define TIFFTAG_ASSHOTWHITEXY                                                  \
-    50729 /* &selected white balance in                                        \
-             x-y chromaticity                                                  \
-             coordinates */
-#define TIFFTAG_BASELINEEXPOSURE                                               \
-    50730                           /* &how much to move the zero              \
-                                       point */
-#define TIFFTAG_BASELINENOISE 50731 /* &relative noise level */
-#define TIFFTAG_BASELINESHARPNESS                                              \
-    50732 /* &relative amount of                                               \
-             sharpening */
-#define TIFFTAG_BAYERGREENSPLIT                                                \
-    50733                                 /* &how closely the values of        \
-                                             the green pixels in the           \
-                                             blue/green rows track the         \
-                                             values of the green pixels        \
-                                             in the red/green rows */
-#define TIFFTAG_LINEARRESPONSELIMIT 50734 /* &non-linear encoding range */
-#define TIFFTAG_CAMERASERIALNUMBER 50735  /* &camera's serial number */
-#define TIFFTAG_LENSINFO 50736            /* info about the lens */
-#define TIFFTAG_CHROMABLURRADIUS 50737    /* &chroma blur radius */
-#define TIFFTAG_ANTIALIASSTRENGTH                                              \
-    50738                            /* &relative strength of the              \
-                                        camera's anti-alias filter */
-#define TIFFTAG_SHADOWSCALE 50739    /* &used by Adobe Camera Raw */
-#define TIFFTAG_DNGPRIVATEDATA 50740 /* &manufacturer's private data */
-#define TIFFTAG_MAKERNOTESAFETY                                                \
-    50741                                    /* &whether the EXIF MakerNote    \
-                                                tag is safe to preserve        \
-                                                along with the rest of the     \
-                                                EXIF data */
-#define TIFFTAG_CALIBRATIONILLUMINANT1 50778 /* &illuminant 1 */
-#define TIFFTAG_CALIBRATIONILLUMINANT2 50779 /* &illuminant 2 */
-#define TIFFTAG_BESTQUALITYSCALE 50780       /* &best quality multiplier */
-#define TIFFTAG_RAWDATAUNIQUEID                                                \
-    50781 /* &unique identifier for                                            \
-             the raw image data */
-#define TIFFTAG_ORIGINALRAWFILENAME                                            \
-    50827 /* &file name of the original                                        \
-             raw file */
-#define TIFFTAG_ORIGINALRAWFILEDATA                                            \
-    50828 /* &contents of the original                                         \
-             raw file */
-#define TIFFTAG_ACTIVEAREA                                                     \
-    50829 /* &active (non-masked) pixels                                       \
-             of the sensor */
-#define TIFFTAG_MASKEDAREAS                                                    \
-    50830                              /* &list of coordinates                 \
-                                          of fully masked pixels */
-#define TIFFTAG_ASSHOTICCPROFILE 50831 /* &these two tags used to */
-#define TIFFTAG_ASSHOTPREPROFILEMATRIX                                         \
-    50832                                     /* map cameras's color space     \
-                                                 into ICC profile space */
+#define TIFFTAG_DNGVERSION 50706           /* &DNG version number */
+#define TIFFTAG_DNGBACKWARDVERSION 50707   /* &DNG compatibility version */
+#define TIFFTAG_UNIQUECAMERAMODEL 50708    /* &name for the camera model */
+#define TIFFTAG_LOCALIZEDCAMERAMODEL 50709 /* &localized camera model name (UTF-8) */
+#define TIFFTAG_CFAPLANECOLOR 50710        /* &CFAPattern->LinearRaw space mapping */
+#define TIFFTAG_CFALAYOUT 50711            /* &spatial layout of the CFA */
+#define TIFFTAG_LINEARIZATIONTABLE 50712   /* &lookup table description */
+#define TIFFTAG_BLACKLEVELREPEATDIM 50713  /* &repeat pattern size for the BlackLevel tag */
+#define TIFFTAG_BLACKLEVEL 50714           /* &zero light encoding level */
+#define TIFFTAG_BLACKLEVELDELTAH 50715     /* &zero light encoding level differences (columns) */
+#define TIFFTAG_BLACKLEVELDELTAV 50716     /* &zero light encoding level differences (rows) */
+#define TIFFTAG_WHITELEVEL 50717           /* &fully saturated encoding level */
+#define TIFFTAG_DEFAULTSCALE 50718         /* &default scale factors */
+#define TIFFTAG_DEFAULTCROPORIGIN 50719    /* &origin of the final image area */
+#define TIFFTAG_DEFAULTCROPSIZE 50720      /* &size of the final image area */
+#define TIFFTAG_COLORMATRIX1 50721         /* &XYZ->reference color space transformation matrix 1 */
+#define TIFFTAG_COLORMATRIX2 50722         /* &XYZ->reference color space transformation matrix 2 */
+#define TIFFTAG_CAMERACALIBRATION1 50723   /* &calibration matrix 1 */
+#define TIFFTAG_CAMERACALIBRATION2 50724   /* &calibration matrix 2 */
+#define TIFFTAG_REDUCTIONMATRIX1 50725     /* &dimensionality reduction matrix 1 */
+#define TIFFTAG_REDUCTIONMATRIX2 50726     /* &dimensionality reduction matrix 2 */
+#define TIFFTAG_ANALOGBALANCE 50727        /* &gain applied the stored raw values*/
+#define TIFFTAG_ASSHOTNEUTRAL 50728        /* &selected white balance in linear reference space */
+#define TIFFTAG_ASSHOTWHITEXY 50729        /* &selected white balance in x-y chromaticity coordinates */
+#define TIFFTAG_BASELINEEXPOSURE 50730     /* &how much to move the zero point */
+#define TIFFTAG_BASELINENOISE 50731        /* &relative noise level */
+#define TIFFTAG_BASELINESHARPNESS 50732    /* &relative amount of sharpening */
+/* TIFFTAG_BAYERGREENSPLIT: &how closely the values of the green pixels in the blue/green rows
+ * track the values of the green pixels in the red/green rows */
+#define TIFFTAG_BAYERGREENSPLIT 50733
+#define TIFFTAG_LINEARRESPONSELIMIT 50734     /* &non-linear encoding range */
+#define TIFFTAG_CAMERASERIALNUMBER 50735      /* &camera's serial number */
+#define TIFFTAG_LENSINFO 50736                /* info about the lens */
+#define TIFFTAG_CHROMABLURRADIUS 50737        /* &chroma blur radius */
+#define TIFFTAG_ANTIALIASSTRENGTH 50738       /* &relative strength of the camera's anti-alias filter */
+#define TIFFTAG_SHADOWSCALE 50739             /* &used by Adobe Camera Raw */
+#define TIFFTAG_DNGPRIVATEDATA 50740          /* &manufacturer's private data */
+#define TIFFTAG_MAKERNOTESAFETY 50741         /* &whether the EXIF MakerNote tag is safe to preserve along with the rest of the EXIF data */
+#define TIFFTAG_CALIBRATIONILLUMINANT1 50778  /* &illuminant 1 */
+#define TIFFTAG_CALIBRATIONILLUMINANT2 50779  /* &illuminant 2 */
+#define TIFFTAG_BESTQUALITYSCALE 50780        /* &best quality multiplier */
+#define TIFFTAG_RAWDATAUNIQUEID 50781         /* &unique identifier for the raw image data */
+#define TIFFTAG_ORIGINALRAWFILENAME 50827     /* &file name of the original raw file (UTF-8) */
+#define TIFFTAG_ORIGINALRAWFILEDATA 50828     /* &contents of the original raw file */
+#define TIFFTAG_ACTIVEAREA 50829              /* &active (non-masked) pixels of the sensor */
+#define TIFFTAG_MASKEDAREAS 50830             /* &list of coordinates of fully masked pixels */
+#define TIFFTAG_ASSHOTICCPROFILE 50831        /* &these two tags used to */
+#define TIFFTAG_ASSHOTPREPROFILEMATRIX 50832  /* map cameras's color space  into ICC profile space */
 #define TIFFTAG_CURRENTICCPROFILE 50833       /* & */
 #define TIFFTAG_CURRENTPREPROFILEMATRIX 50834 /* & */
 
-#define TIFFTAG_RPCCOEFFICIENT                                                 \
-    50844 /* Define by GDAL for geospatial georeferencing through RPC:         \
-             http://geotiff.maptools.org/rpc_prop.html */
+/* DNG 1.2.0.0 */
+#define TIFFTAG_COLORIMETRICREFERENCE 50879       /* &colorimetric reference */
+#define TIFFTAG_CAMERACALIBRATIONSIGNATURE 50931  /* &camera calibration signature (UTF-8) */
+#define TIFFTAG_PROFILECALIBRATIONSIGNATURE 50932 /* &profile calibration signature (UTF-8) */
+/* TIFFTAG_EXTRACAMERAPROFILES 50933 &extra camera profiles : is already defined for GeoTIFF DGIWG */
+#define TIFFTAG_ASSHOTPROFILENAME 50934           /* &as shot profile name (UTF-8) */
+#define TIFFTAG_NOISEREDUCTIONAPPLIED 50935       /* &amount of applied noise reduction */
+#define TIFFTAG_PROFILENAME 50936                 /* &camera profile name (UTF-8) */
+#define TIFFTAG_PROFILEHUESATMAPDIMS 50937        /* &dimensions of HSV mapping */
+#define TIFFTAG_PROFILEHUESATMAPDATA1 50938       /* &first HSV mapping table */
+#define TIFFTAG_PROFILEHUESATMAPDATA2 50939       /* &second HSV mapping table */
+#define TIFFTAG_PROFILETONECURVE 50940            /* &default tone curve */
+#define TIFFTAG_PROFILEEMBEDPOLICY 50941          /* &profile embedding policy */
+#define TIFFTAG_PROFILECOPYRIGHT 50942            /* &profile copyright information (UTF-8) */
+#define TIFFTAG_FORWARDMATRIX1 50964              /* &matrix for mapping white balanced camera colors to XYZ D50 */
+#define TIFFTAG_FORWARDMATRIX2 50965              /* &matrix for mapping white balanced camera colors to XYZ D50 */
+#define TIFFTAG_PREVIEWAPPLICATIONNAME 50966      /* &name of application that created preview (UTF-8) */
+#define TIFFTAG_PREVIEWAPPLICATIONVERSION 50967   /* &version of application that created preview (UTF-8) */
+#define TIFFTAG_PREVIEWSETTINGSNAME 50968         /* &name of conversion settings (UTF-8) */
+#define TIFFTAG_PREVIEWSETTINGSDIGEST 50969       /* &unique id of conversion settings */
+#define TIFFTAG_PREVIEWCOLORSPACE 50970           /* &preview color space */
+#define TIFFTAG_PREVIEWDATETIME 50971             /* &date/time preview was rendered */
+#define TIFFTAG_RAWIMAGEDIGEST 50972              /* &md5 of raw image data */
+#define TIFFTAG_ORIGINALRAWFILEDIGEST 50973       /* &md5 of the data stored in the OriginalRawFileData tag */
+#define TIFFTAG_SUBTILEBLOCKSIZE 50974            /* &subtile block size */
+#define TIFFTAG_ROWINTERLEAVEFACTOR 50975         /* &number of interleaved fields */
+#define TIFFTAG_PROFILELOOKTABLEDIMS 50981        /* &num of input samples in each dim of default "look" table */
+#define TIFFTAG_PROFILELOOKTABLEDATA 50982        /* &default "look" table for use as starting point */
 
-#define TIFFTAG_ALIAS_LAYER_METADATA                                           \
-    50784 /* Alias Sketchbook Pro layer usage description. */
+/* DNG 1.3.0.0 */
+#define TIFFTAG_OPCODELIST1 51008  /* &opcodes that should be applied to raw image after reading */
+#define TIFFTAG_OPCODELIST2 51009  /* &opcodes that should be applied after mapping to linear reference */
+#define TIFFTAG_OPCODELIST3 51022  /* &opcodes that should be applied after demosaicing */
+#define TIFFTAG_NOISEPROFILE 51041 /* &noise profile */
+
+/* DNG 1.4.0.0 */
+#define TIFFTAG_DEFAULTUSERCROP 51125              /* &default user crop rectangle in relative coords */
+#define TIFFTAG_DEFAULTBLACKRENDER 51110           /* &black rendering hint */
+#define TIFFTAG_BASELINEEXPOSUREOFFSET 51109       /* &baseline exposure offset */
+#define TIFFTAG_PROFILELOOKTABLEENCODING 51108     /* &3D LookTable indexing conversion */
+#define TIFFTAG_PROFILEHUESATMAPENCODING 51107     /* &3D HueSatMap indexing conversion */
+#define TIFFTAG_ORIGINALDEFAULTFINALSIZE 51089     /* &default final size of larger original file for this proxy */
+#define TIFFTAG_ORIGINALBESTQUALITYFINALSIZE 51090 /* &best quality final size of larger original file for this proxy */
+#define TIFFTAG_ORIGINALDEFAULTCROPSIZE 51091      /* &the default crop size of larger original file for this proxy */
+#define TIFFTAG_NEWRAWIMAGEDIGEST 51111            /* &modified MD5 digest of the raw image data */
+#define TIFFTAG_RAWTOPREVIEWGAIN 51112             /* &The gain between the main raw FD and the preview IFD containing this tag */
+
+/* DNG 1.5.0.0 */
+#define TIFFTAG_DEPTHFORMAT 51177      /* &encoding of the depth data in the file */
+#define TIFFTAG_DEPTHNEAR 51178        /* &distance from the camera represented by value 0 in the depth map */
+#define TIFFTAG_DEPTHFAR 51179         /* &distance from the camera represented by the maximum value in the depth map */
+#define TIFFTAG_DEPTHUNITS 51180       /* &measurement units for DepthNear and DepthFar */
+#define TIFFTAG_DEPTHMEASURETYPE 51181 /* &measurement geometry for the depth map */
+#define TIFFTAG_ENHANCEPARAMS 51182    /* &a string that documents how the enhanced image data was processed. */
+
+/* DNG 1.6.0.0 */
+#define TIFFTAG_PROFILEGAINTABLEMAP 52525    /* &spatially varying gain tables that can be applied as starting point */
+#define TIFFTAG_SEMANTICNAME 52526           /* &a string that identifies the semantic mask */
+#define TIFFTAG_SEMANTICINSTANCEID 52528     /* &a string that identifies a specific instance in a semantic mask */
+#define TIFFTAG_MASKSUBAREA 52536            /* &the crop rectangle of this IFD's mask, relative to the main image */
+#define TIFFTAG_RGBTABLES 52543              /* &color transforms to apply to masked image regions */
+#define TIFFTAG_CALIBRATIONILLUMINANT3 52529 /* &the illuminant used for the third set of color calibration tags */
+#define TIFFTAG_COLORMATRIX3 52531           /* &matrix to convert XYZ values to reference camera native color space under CalibrationIlluminant3 */
+#define TIFFTAG_CAMERACALIBRATION3 52530     /* &matrix to transform reference camera native space values to individual camera native space values under CalibrationIlluminant3 */
+#define TIFFTAG_REDUCTIONMATRIX3 52538       /* &dimensionality reduction matrix for use in color conversion to XYZ under CalibrationIlluminant3 */
+#define TIFFTAG_PROFILEHUESATMAPDATA3 52537  /* &the data for the third HSV table */
+#define TIFFTAG_FORWARDMATRIX3 52532         /* &matrix to map white balanced camera colors to XYZ D50 */
+#define TIFFTAG_ILLUMINANTDATA1 52533        /* &data for the first calibration illuminant */
+#define TIFFTAG_ILLUMINANTDATA2 52534        /* &data for the second calibration illuminant */
+#define TIFFTAG_ILLUMINANTDATA3 53535        /* &data for the third calibration illuminant */
+
+/* TIFF/EP */
+#define TIFFTAG_EP_CFAREPEATPATTERNDIM 33421      /* dimensions of CFA pattern */
+#define TIFFTAG_EP_CFAPATTERN 33422               /* color filter array pattern */
+#define TIFFTAG_EP_BATTERYLEVEL 33423             /* battery level (rational or ASCII) */
+#define TIFFTAG_EP_INTERLACE 34857                /* Number of multi-field images */
+/* TIFFTAG_EP_IPTC_NAA and TIFFTAG_RICHTIFFIPTC share the same tag number (33723)
+ *   LibTIFF type is UNDEFINED or BYTE, but often times incorrectly specified as LONG,
+ *   because TIFF/EP (ISO/DIS 12234-2) specifies type LONG or ASCII. */
+#define TIFFTAG_EP_IPTC_NAA 33723                 /* Alias IPTC/NAA Newspaper Association RichTIFF */
+#define TIFFTAG_EP_TIMEZONEOFFSET 34858           /* Time zone offset relative to UTC */
+#define TIFFTAG_EP_SELFTIMERMODE 34859            /* Number of seconds capture was delayed from button press */
+#define TIFFTAG_EP_FLASHENERGY 37387              /* Flash energy, or range if there is uncertainty */
+#define TIFFTAG_EP_SPATIALFREQUENCYRESPONSE 37388 /* Spatial frequency response */
+#define TIFFTAG_EP_NOISE 37389                    /* Camera noise measurement values */
+#define TIFFTAG_EP_FOCALPLANEXRESOLUTION 37390    /* Focal plane X resolution */
+#define TIFFTAG_EP_FOCALPLANEYRESOLUTION 37391    /* Focal plane Y resolution */
+#define TIFFTAG_EP_FOCALPLANERESOLUTIONUNIT 37392 /* Focal plane resolution unit */
+#define TIFFTAG_EP_IMAGENUMBER 37393              /* Number of image when several of burst shot stored in same TIFF/EP */
+#define TIFFTAG_EP_SECURITYCLASSIFICATION 37394   /* Security classification */
+#define TIFFTAG_EP_IMAGEHISTORY 37395             /* Record of what has been done to the image */
+#define TIFFTAG_EP_EXPOSUREINDEX 37397            /* Exposure index */
+#define TIFFTAG_EP_STANDARDID 37398               /* TIFF/EP standard version, n.n.n.n */
+#define TIFFTAG_EP_SENSINGMETHOD 37399            /* Type of image sensor */
+/* 
+ * TIFF/EP tags equivalent to EXIF tags
+ *     Note that TIFF-EP and EXIF use nearly the same metadata tag set, but TIFF-EP stores the tags in IFD 0,
+ *     while EXIF store the tags in a separate IFD. Either location is allowed by DNG, but the EXIF location is preferred.
+ */
+#define TIFFTAG_EP_EXPOSURETIME 33434             /* Exposure time */
+#define TIFFTAG_EP_FNUMBER 33437                  /* F number */
+#define TIFFTAG_EP_EXPOSUREPROGRAM 34850          /* Exposure program */
+#define TIFFTAG_EP_SPECTRALSENSITIVITY 34852      /* Spectral sensitivity */
+#define TIFFTAG_EP_ISOSPEEDRATINGS 34855          /* ISO speed rating */
+#define TIFFTAG_EP_OECF 34856                     /* Optoelectric conversion factor */
+#define TIFFTAG_EP_DATETIMEORIGINAL 36867         /* Date and time of original data generation */
+#define TIFFTAG_EP_COMPRESSEDBITSPERPIXEL 37122   /* Image compression mode */
+#define TIFFTAG_EP_SHUTTERSPEEDVALUE 37377        /* Shutter speed */
+#define TIFFTAG_EP_APERTUREVALUE 37378            /* Aperture */
+#define TIFFTAG_EP_BRIGHTNESSVALUE 37379          /* Brightness */
+#define TIFFTAG_EP_EXPOSUREBIASVALUE 37380        /* Exposure bias */
+#define TIFFTAG_EP_MAXAPERTUREVALUE 37381         /* Maximum lens aperture */
+#define TIFFTAG_EP_SUBJECTDISTANCE 37382          /* Subject distance */
+#define TIFFTAG_EP_METERINGMODE 37383             /* Metering mode */
+#define TIFFTAG_EP_LIGHTSOURCE 37384              /* Light source */
+#define TIFFTAG_EP_FLASH 37385                    /* Flash */
+#define TIFFTAG_EP_FOCALLENGTH 37386              /* Lens focal length */
+#define TIFFTAG_EP_SUBJECTLOCATION 37396          /* Subject location (area) */
+
+#define TIFFTAG_RPCCOEFFICIENT 50844       /* Define by GDAL for geospatial georeferencing through RPC: http://geotiff.maptools.org/rpc_prop.html */
+#define TIFFTAG_ALIAS_LAYER_METADATA 50784 /* Alias Sketchbook Pro layer usage description. */
 
 /* GeoTIFF DGIWG */
-#define TIFFTAG_TIFF_RSID                                                      \
-    50908 /* https://www.awaresystems.be/imaging/tiff/tifftags/tiff_rsid.html  \
-           */
-#define TIFFTAG_GEO_METADATA                                                     \
-    50909 /* https://www.awaresystems.be/imaging/tiff/tifftags/geo_metadata.html \
-           */
-
-#define TIFFTAG_EXTRACAMERAPROFILES                                                                                     \
-    50933 /* http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf \
-           */
+#define TIFFTAG_TIFF_RSID 50908           /* https://www.awaresystems.be/imaging/tiff/tifftags/tiff_rsid.html */
+#define TIFFTAG_GEO_METADATA 50909        /* https://www.awaresystems.be/imaging/tiff/tifftags/geo_metadata.html */
+#define TIFFTAG_EXTRACAMERAPROFILES 50933 /* http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf */
 
 /* tag 65535 is an undefined tag used by Eastman Kodak */
 #define TIFFTAG_DCSHUESHIFTVALUES 65535 /* hue shift correction data */
@@ -751,220 +758,141 @@
 #define LERC_ADD_COMPRESSION_NONE 0
 #define LERC_ADD_COMPRESSION_DEFLATE 1
 #define LERC_ADD_COMPRESSION_ZSTD 2
-#define TIFFTAG_LERC_MAXZERROR 65567 /* LERC maximum error */
-#define TIFFTAG_WEBP_LEVEL 65568     /* WebP compression level */
-#define TIFFTAG_WEBP_LOSSLESS 65569  /* WebP lossless/lossy */
-#define TIFFTAG_DEFLATE_SUBCODEC                                               \
-    65570 /* ZIP codec: to get/set the sub-codec to use. Will default to       \
-             libdeflate when available */
+#define TIFFTAG_LERC_MAXZERROR 65567   /* LERC maximum error */
+#define TIFFTAG_WEBP_LEVEL 65568       /* WebP compression level */
+#define TIFFTAG_WEBP_LOSSLESS 65569    /* WebP lossless/lossy */
+#define TIFFTAG_DEFLATE_SUBCODEC 65570 /* ZIP codec: to get/set the sub-codec to use. Will default to libdeflate when available */
 #define DEFLATE_SUBCODEC_ZLIB 0
 #define DEFLATE_SUBCODEC_LIBDEFLATE 1
 
 /*
  * EXIF tags
  */
-#define EXIFTAG_EXPOSURETIME 33434        /* Exposure time */
-#define EXIFTAG_FNUMBER 33437             /* F number */
-#define EXIFTAG_EXPOSUREPROGRAM 34850     /* Exposure program */
-#define EXIFTAG_SPECTRALSENSITIVITY 34852 /* Spectral sensitivity */
-#define EXIFTAG_ISOSPEEDRATINGS 34855     /* ISO speed rating */
-#define EXIFTAG_PHOTOGRAPHICSENSITIVITY                                        \
-    34855 /* Photographic Sensitivity (new name for tag 34855) */
-#define EXIFTAG_OECF 34856        /* Optoelectric conversion factor */
-#define EXIFTAG_EXIFVERSION 36864 /* Exif version */
-#define EXIFTAG_DATETIMEORIGINAL                                               \
-    36867 /* Date and time of original                                         \
-             data generation */
-#define EXIFTAG_DATETIMEDIGITIZED                                              \
-    36868                                     /* Date and time of digital      \
-                                                 data generation */
-#define EXIFTAG_COMPONENTSCONFIGURATION 37121 /* Meaning of each component */
-#define EXIFTAG_COMPRESSEDBITSPERPIXEL 37122  /* Image compression mode */
-#define EXIFTAG_SHUTTERSPEEDVALUE 37377       /* Shutter speed */
-#define EXIFTAG_APERTUREVALUE 37378           /* Aperture */
-#define EXIFTAG_BRIGHTNESSVALUE 37379         /* Brightness */
-#define EXIFTAG_EXPOSUREBIASVALUE 37380       /* Exposure bias */
-#define EXIFTAG_MAXAPERTUREVALUE 37381        /* Maximum lens aperture */
-#define EXIFTAG_SUBJECTDISTANCE 37382         /* Subject distance */
-#define EXIFTAG_METERINGMODE 37383            /* Metering mode */
-#define EXIFTAG_LIGHTSOURCE 37384             /* Light source */
-#define EXIFTAG_FLASH 37385                   /* Flash */
-#define EXIFTAG_FOCALLENGTH 37386             /* Lens focal length */
-#define EXIFTAG_SUBJECTAREA 37396             /* Subject area */
-#define EXIFTAG_MAKERNOTE 37500               /* Manufacturer notes */
-#define EXIFTAG_USERCOMMENT 37510             /* User comments */
-#define EXIFTAG_SUBSECTIME 37520              /* DateTime subseconds */
-#define EXIFTAG_SUBSECTIMEORIGINAL 37521      /* DateTimeOriginal subseconds */
-#define EXIFTAG_SUBSECTIMEDIGITIZED 37522     /* DateTimeDigitized subseconds */
-#define EXIFTAG_FLASHPIXVERSION 40960         /* Supported Flashpix version */
-#define EXIFTAG_COLORSPACE 40961              /* Color space information */
-#define EXIFTAG_PIXELXDIMENSION 40962         /* Valid image width */
-#define EXIFTAG_PIXELYDIMENSION 40963         /* Valid image height */
-#define EXIFTAG_RELATEDSOUNDFILE 40964        /* Related audio file */
-#define EXIFTAG_FLASHENERGY 41483             /* Flash energy */
-#define EXIFTAG_SPATIALFREQUENCYRESPONSE                                       \
-    41484                                   /* Spatial frequency response      \
-                                             */
-#define EXIFTAG_FOCALPLANEXRESOLUTION 41486 /* Focal plane X resolution */
-#define EXIFTAG_FOCALPLANEYRESOLUTION 41487 /* Focal plane Y resolution */
-#define EXIFTAG_FOCALPLANERESOLUTIONUNIT                                       \
-    41488                                   /* Focal plane resolution unit     \
-                                             */
-#define EXIFTAG_SUBJECTLOCATION 41492       /* Subject location */
-#define EXIFTAG_EXPOSUREINDEX 41493         /* Exposure index */
-#define EXIFTAG_SENSINGMETHOD 41495         /* Sensing method */
-#define EXIFTAG_FILESOURCE 41728            /* File source */
-#define EXIFTAG_SCENETYPE 41729             /* Scene type */
-#define EXIFTAG_CFAPATTERN 41730            /* CFA pattern */
-#define EXIFTAG_CUSTOMRENDERED 41985        /* Custom image processing */
-#define EXIFTAG_EXPOSUREMODE 41986          /* Exposure mode */
-#define EXIFTAG_WHITEBALANCE 41987          /* White balance */
-#define EXIFTAG_DIGITALZOOMRATIO 41988      /* Digital zoom ratio */
-#define EXIFTAG_FOCALLENGTHIN35MMFILM 41989 /* Focal length in 35 mm film */
-#define EXIFTAG_SCENECAPTURETYPE 41990      /* Scene capture type */
-#define EXIFTAG_GAINCONTROL 41991           /* Gain control */
-#define EXIFTAG_CONTRAST 41992              /* Contrast */
-#define EXIFTAG_SATURATION 41993            /* Saturation */
-#define EXIFTAG_SHARPNESS 41994             /* Sharpness */
-#define EXIFTAG_DEVICESETTINGDESCRIPTION                                       \
-    41995                                  /* Device settings description      \
-                                            */
-#define EXIFTAG_SUBJECTDISTANCERANGE 41996 /* Subject distance range */
-#define EXIFTAG_IMAGEUNIQUEID 42016        /* Unique image ID */
+#define EXIFTAG_EXPOSURETIME 33434             /* Exposure time */
+#define EXIFTAG_FNUMBER 33437                  /* F number */
+#define EXIFTAG_EXPOSUREPROGRAM 34850          /* Exposure program */
+#define EXIFTAG_SPECTRALSENSITIVITY 34852      /* Spectral sensitivity */
+/* After EXIF 2.2.1 ISOSpeedRatings is named PhotographicSensitivity.
+   In addition, while "Count=Any", only 1 count should be used. */
+#define EXIFTAG_ISOSPEEDRATINGS 34855          /* ISO speed rating */
+#define EXIFTAG_PHOTOGRAPHICSENSITIVITY 34855  /* Photographic Sensitivity (new name for tag 34855) */
+#define EXIFTAG_OECF 34856                     /* Optoelectric conversion factor */
+#define EXIFTAG_EXIFVERSION 36864              /* Exif version */
+#define EXIFTAG_DATETIMEORIGINAL 36867         /* Date and time of original data generation */
+#define EXIFTAG_DATETIMEDIGITIZED 36868        /* Date and time of digital data generation */
+#define EXIFTAG_COMPONENTSCONFIGURATION 37121  /* Meaning of each component */
+#define EXIFTAG_COMPRESSEDBITSPERPIXEL 37122   /* Image compression mode */
+#define EXIFTAG_SHUTTERSPEEDVALUE 37377        /* Shutter speed */
+#define EXIFTAG_APERTUREVALUE 37378            /* Aperture */
+#define EXIFTAG_BRIGHTNESSVALUE 37379          /* Brightness */
+#define EXIFTAG_EXPOSUREBIASVALUE 37380        /* Exposure bias */
+#define EXIFTAG_MAXAPERTUREVALUE 37381         /* Maximum lens aperture */
+#define EXIFTAG_SUBJECTDISTANCE 37382          /* Subject distance */
+#define EXIFTAG_METERINGMODE 37383             /* Metering mode */
+#define EXIFTAG_LIGHTSOURCE 37384              /* Light source */
+#define EXIFTAG_FLASH 37385                    /* Flash */
+#define EXIFTAG_FOCALLENGTH 37386              /* Lens focal length */
+#define EXIFTAG_SUBJECTAREA 37396              /* Subject area */
+#define EXIFTAG_MAKERNOTE 37500                /* Manufacturer notes */
+#define EXIFTAG_USERCOMMENT 37510              /* User comments */
+#define EXIFTAG_SUBSECTIME 37520               /* DateTime subseconds */
+#define EXIFTAG_SUBSECTIMEORIGINAL 37521       /* DateTimeOriginal subseconds */
+#define EXIFTAG_SUBSECTIMEDIGITIZED 37522      /* DateTimeDigitized subseconds */
+#define EXIFTAG_FLASHPIXVERSION 40960          /* Supported Flashpix version */
+#define EXIFTAG_COLORSPACE 40961               /* Color space information */
+#define EXIFTAG_PIXELXDIMENSION 40962          /* Valid image width */
+#define EXIFTAG_PIXELYDIMENSION 40963          /* Valid image height */
+#define EXIFTAG_RELATEDSOUNDFILE 40964         /* Related audio file */
+#define EXIFTAG_FLASHENERGY 41483              /* Flash energy */
+#define EXIFTAG_SPATIALFREQUENCYRESPONSE 41484 /* Spatial frequency response */
+#define EXIFTAG_FOCALPLANEXRESOLUTION 41486    /* Focal plane X resolution */
+#define EXIFTAG_FOCALPLANEYRESOLUTION 41487    /* Focal plane Y resolution */
+#define EXIFTAG_FOCALPLANERESOLUTIONUNIT 41488 /* Focal plane resolution unit */
+#define EXIFTAG_SUBJECTLOCATION 41492          /* Subject location */
+#define EXIFTAG_EXPOSUREINDEX 41493            /* Exposure index */
+#define EXIFTAG_SENSINGMETHOD 41495            /* Sensing method */
+#define EXIFTAG_FILESOURCE 41728               /* File source */
+#define EXIFTAG_SCENETYPE 41729                /* Scene type */
+#define EXIFTAG_CFAPATTERN 41730               /* CFA pattern */
+#define EXIFTAG_CUSTOMRENDERED 41985           /* Custom image processing */
+#define EXIFTAG_EXPOSUREMODE 41986             /* Exposure mode */
+#define EXIFTAG_WHITEBALANCE 41987             /* White balance */
+#define EXIFTAG_DIGITALZOOMRATIO 41988         /* Digital zoom ratio */
+#define EXIFTAG_FOCALLENGTHIN35MMFILM 41989    /* Focal length in 35 mm film */
+#define EXIFTAG_SCENECAPTURETYPE 41990         /* Scene capture type */
+#define EXIFTAG_GAINCONTROL 41991              /* Gain control */
+#define EXIFTAG_CONTRAST 41992                 /* Contrast */
+#define EXIFTAG_SATURATION 41993               /* Saturation */
+#define EXIFTAG_SHARPNESS 41994                /* Sharpness */
+#define EXIFTAG_DEVICESETTINGDESCRIPTION 41995 /* Device settings description */
+#define EXIFTAG_SUBJECTDISTANCERANGE 41996     /* Subject distance range */
+#define EXIFTAG_IMAGEUNIQUEID 42016            /* Unique image ID */
 
 /*--: New for EXIF-Version 2.32, May 2019 ... */
-#define EXIFTAG_SENSITIVITYTYPE                                                \
-    34864 /* The SensitivityType tag indicates which one of the parameters of  \
-             ISO12232 is the PhotographicSensitivity tag. */
-#define EXIFTAG_STANDARDOUTPUTSENSITIVITY                                      \
-    34865 /* This tag indicates the standard output sensitivity value of a     \
-             camera or input device defined in ISO 12232. */
-#define EXIFTAG_RECOMMENDEDEXPOSUREINDEX                                       \
-    34866                                 /* recommended exposure index        \
-                                           */
-#define EXIFTAG_ISOSPEED 34867            /* ISO speed value */
-#define EXIFTAG_ISOSPEEDLATITUDEYYY 34868 /* ISO speed latitude yyy */
-#define EXIFTAG_ISOSPEEDLATITUDEZZZ 34869 /* ISO speed latitude zzz */
-#define EXIFTAG_OFFSETTIME                                                     \
-    36880 /* offset from UTC of the time of DateTime tag. */
-#define EXIFTAG_OFFSETTIMEORIGINAL                                             \
-    36881 /* offset from UTC of the time of DateTimeOriginal tag. */
-#define EXIFTAG_OFFSETTIMEDIGITIZED                                            \
-    36882 /* offset from UTC of the time of DateTimeDigitized tag. */
-#define EXIFTAG_TEMPERATURE                                                    \
-    37888 /* Temperature as the ambient situation at the shot in dergee        \
-             Celsius */
-#define EXIFTAG_HUMIDITY                                                       \
-    37889 /* Humidity as the ambient situation at the shot in percent */
-#define EXIFTAG_PRESSURE                                                       \
-    37890 /* Pressure as the ambient situation at the shot hecto-Pascal (hPa)  \
-           */
-#define EXIFTAG_WATERDEPTH                                                     \
-    37891 /* WaterDepth as the ambient situation at the shot in meter (m) */
-#define EXIFTAG_ACCELERATION                                                   \
-    37892 /* Acceleration (a scalar regardless of direction) as the ambient    \
-             situation at the shot in units of mGal (10-5 m/s^2) */
-#define EXIFTAG_CAMERAELEVATIONANGLE                                           \
-    37893 /* Elevation/depression. angle of the orientation of the             \
-             camera(imaging optical axis) as the ambient situation at the shot \
-             in degree from -180deg to +180deg. */
-#define EXIFTAG_CAMERAOWNERNAME 42032 /* owner of a camera */
-#define EXIFTAG_BODYSERIALNUMBER                                               \
-    42033 /* serial number of the body of the camera */
-#define EXIFTAG_LENSSPECIFICATION                                              \
-    42034 /* minimum focal length (in mm), maximum focal length (in mm),       \
-             minimum F number in the minimum focal length, and minimum F       \
-             number in the maximum focal length, */
-#define EXIFTAG_LENSMAKE 42035  /* the lens manufacturer */
-#define EXIFTAG_LENSMODEL 42036 /* the lens model name and model number */
-#define EXIFTAG_LENSSERIALNUMBER                                               \
-    42037                   /* the serial number of the interchangeable lens */
-#define EXIFTAG_GAMMA 42240 /* value of coefficient gamma */
-#define EXIFTAG_COMPOSITEIMAGE 42080 /* composite image */
-#define EXIFTAG_SOURCEIMAGENUMBEROFCOMPOSITEIMAGE                              \
-    42081 /* source image number of composite image */
-#define EXIFTAG_SOURCEEXPOSURETIMESOFCOMPOSITEIMAGE                            \
-    42082 /* source exposure times of composite image */
+#define EXIFTAG_SENSITIVITYTYPE 34864           /* The SensitivityType tag indicates which one of the parameters of ISO12232 is the PhotographicSensitivity tag. */
+#define EXIFTAG_STANDARDOUTPUTSENSITIVITY 34865 /* This tag indicates the standard output sensitivity value of a camera or input device defined in ISO 12232. */
+#define EXIFTAG_RECOMMENDEDEXPOSUREINDEX 34866  /* recommended exposure index   */
+#define EXIFTAG_ISOSPEED 34867                  /* ISO speed value */
+#define EXIFTAG_ISOSPEEDLATITUDEYYY 34868       /* ISO speed latitude yyy */
+#define EXIFTAG_ISOSPEEDLATITUDEZZZ 34869       /* ISO speed latitude zzz */
+#define EXIFTAG_OFFSETTIME 36880                /* offset from UTC of the time of DateTime tag. */
+#define EXIFTAG_OFFSETTIMEORIGINAL 36881        /* offset from UTC of the time of DateTimeOriginal tag. */
+#define EXIFTAG_OFFSETTIMEDIGITIZED 36882       /* offset from UTC of the time of DateTimeDigitized tag. */
+#define EXIFTAG_TEMPERATURE 37888               /* Temperature as the ambient situation at the shot in dergee Celsius */
+#define EXIFTAG_HUMIDITY 37889                  /* Humidity as the ambient situation at the shot in percent */
+#define EXIFTAG_PRESSURE 37890                  /* Pressure as the ambient situation at the shot hecto-Pascal (hPa) */
+#define EXIFTAG_WATERDEPTH 37891                /* WaterDepth as the ambient situation at the shot in meter (m) */
+#define EXIFTAG_ACCELERATION 37892              /* Acceleration (a scalar regardless of direction) as the ambientsituation at the shot in units of mGal (10-5 m/s^2) */
+/* EXIFTAG_CAMERAELEVATIONANGLE: Elevation/depression. angle of the orientation of the  camera(imaging optical axis)
+ *                               as the ambient situation at the shot in degree from -180deg to +180deg. */
+#define EXIFTAG_CAMERAELEVATIONANGLE 37893
+#define EXIFTAG_CAMERAOWNERNAME 42032  /* owner of a camera */
+#define EXIFTAG_BODYSERIALNUMBER 42033 /* serial number of the body of the camera */
+/* EXIFTAG_LENSSPECIFICATION: minimum focal length (in mm), maximum focal length (in mm),minimum F number in the minimum focal length,
+ *                            and minimum F number in the maximum focal length, */
+#define EXIFTAG_LENSSPECIFICATION 42034
+#define EXIFTAG_LENSMAKE 42035                            /* the lens manufacturer */
+#define EXIFTAG_LENSMODEL 42036                           /* the lens model name and model number */
+#define EXIFTAG_LENSSERIALNUMBER 42037                    /* the serial number of the interchangeable lens */
+#define EXIFTAG_GAMMA 42240                               /* value of coefficient gamma */
+#define EXIFTAG_COMPOSITEIMAGE 42080                      /* composite image */
+#define EXIFTAG_SOURCEIMAGENUMBEROFCOMPOSITEIMAGE 42081   /* source image number of composite image */
+#define EXIFTAG_SOURCEEXPOSURETIMESOFCOMPOSITEIMAGE 42082 /* source exposure times of composite image */
 
 /*
  * EXIF-GPS tags  (Version 2.31, July 2016)
  */
-#define GPSTAG_VERSIONID 0 /* 	Indicates the version of GPSInfoIFD.	 */
-#define GPSTAG_LATITUDEREF                                                     \
-    1 /* 	Indicates whether the latitude is north or south latitude.           \
-       */
-#define GPSTAG_LATITUDE 2 /* 	Indicates the latitude.	 */
-#define GPSTAG_LONGITUDEREF                                                    \
-    3 /* 	Indicates whether the longitude is east or west longitude.           \
-       */
-#define GPSTAG_LONGITUDE 4 /* 	Indicates the longitude.	 */
-#define GPSTAG_ALTITUDEREF                                                     \
-    5 /* 	Indicates the altitude used as the reference altitude.	 */
-#define GPSTAG_ALTITUDE                                                        \
-    6 /* 	Indicates the altitude based on the reference in                     \
-         GPSAltitudeRef.	 */
-#define GPSTAG_TIMESTAMP                                                       \
-    7 /* 	Indicates the time as UTC (Coordinated Universal Time).	 */
-#define GPSTAG_SATELLITES                                                      \
-    8 /* 	Indicates the GPS satellites used for measurements.	 */
-#define GPSTAG_STATUS                                                          \
-    9 /* 	Indicates the status of the GPS receiver when the image is           \
-         recorded.	 */
-#define GPSTAG_MEASUREMODE                                                     \
-    10 /* 	Indicates the GPS measurement mode.	 */
-#define GPSTAG_DOP                                                             \
-    11 /* 	Indicates the GPS DOP (data degree of precision).	 */
-#define GPSTAG_SPEEDREF                                                        \
-    12 /* 	Indicates the unit used to express the GPS receiver speed of        \
-          movement.	 */
-#define GPSTAG_SPEED                                                           \
-    13 /* 	Indicates the speed of GPS receiver movement.	 */
-#define GPSTAG_TRACKREF                                                        \
-    14 /* 	Indicates the reference for giving the direction of GPS             \
-          receiver movement.	 */
-#define GPSTAG_TRACK                                                           \
-    15 /* 	Indicates the direction of GPS receiver movement.	 */
-#define GPSTAG_IMGDIRECTIONREF                                                 \
-    16 /* 	Indicates the reference for giving the direction of the image       \
-          when it is captured.	 */
-#define GPSTAG_IMGDIRECTION                                                    \
-    17 /* 	Indicates the direction of the image when it was captured.          \
-        */
-#define GPSTAG_MAPDATUM                                                        \
-    18 /* 	Indicates the geodetic survey data used by the GPS receiver.        \
-          (e.g. WGS-84)	 */
-#define GPSTAG_DESTLATITUDEREF                                                 \
-    19 /* 	Indicates whether the latitude of the destination point is          \
-          north or south latitude.	 */
-#define GPSTAG_DESTLATITUDE                                                    \
-    20 /* 	Indicates the latitude of the destination point.	 */
-#define GPSTAG_DESTLONGITUDEREF                                                \
-    21 /* 	Indicates whether the longitude of the destination point is         \
-          east or west longitude.	 */
-#define GPSTAG_DESTLONGITUDE                                                   \
-    22 /* 	Indicates the longitude of the destination point.	 */
-#define GPSTAG_DESTBEARINGREF                                                  \
-    23 /* 	Indicates the reference used for giving the bearing to the          \
-          destination point.	 */
-#define GPSTAG_DESTBEARING                                                     \
-    24 /* 	Indicates the bearing to the destination point.	 */
-#define GPSTAG_DESTDISTANCEREF                                                 \
-    25 /* 	Indicates the unit used to express the distance to the              \
-          destination point.	 */
-#define GPSTAG_DESTDISTANCE                                                    \
-    26 /* 	Indicates the distance to the destination point.	 */
-#define GPSTAG_PROCESSINGMETHOD                                                \
-    27 /* 	A character string recording the name of the method used for        \
-          location finding.	 */
-#define GPSTAG_AREAINFORMATION                                                 \
-    28 /* 	A character string recording the name of the GPS area.	 */
-#define GPSTAG_DATESTAMP                                                       \
-    29 /* 	A character string recording date and time information              \
-          relative to UTC (Coordinated Universal Time).	 */
-#define GPSTAG_DIFFERENTIAL                                                    \
-    30 /* 	Indicates whether differential correction is applied to the         \
-          GPS receiver.	 */
-#define GPSTAG_GPSHPOSITIONINGERROR                                            \
-    31 /* Indicates horizontal positioning errors in meters.		 */
+#define GPSTAG_VERSIONID 0             /* Indicates the version of GPSInfoIFD. */
+#define GPSTAG_LATITUDEREF 1           /* Indicates whether the latitude is north or south latitude. */
+#define GPSTAG_LATITUDE 2              /* Indicates the latitude. */
+#define GPSTAG_LONGITUDEREF 3          /* Indicates whether the longitude is east or west longitude. */
+#define GPSTAG_LONGITUDE 4             /* Indicates the longitude. */
+#define GPSTAG_ALTITUDEREF 5           /* Indicates the altitude used as the reference altitude. */
+#define GPSTAG_ALTITUDE 6              /* Indicates the altitude based on the reference in GPSAltitudeRef. */
+#define GPSTAG_TIMESTAMP 7             /*Indicates the time as UTC (Coordinated Universal Time). */
+#define GPSTAG_SATELLITES 8            /*Indicates the GPS satellites used for measurements. */
+#define GPSTAG_STATUS 9                /* Indicates the status of the GPS receiver when the image is  recorded. */
+#define GPSTAG_MEASUREMODE 10          /* Indicates the GPS measurement mode. */
+#define GPSTAG_DOP 11                  /* Indicates the GPS DOP (data degree of precision). */
+#define GPSTAG_SPEEDREF 12             /* Indicates the unit used to express the GPS receiver speed of movement. */
+#define GPSTAG_SPEED 13                /* Indicates the speed of GPS receiver movement. */
+#define GPSTAG_TRACKREF 14             /* Indicates the reference for giving the direction of GPS receiver movement. */
+#define GPSTAG_TRACK 15                /* Indicates the direction of GPS receiver movement. */
+#define GPSTAG_IMGDIRECTIONREF 16      /* Indicates the reference for giving the direction of the image when it is captured. */
+#define GPSTAG_IMGDIRECTION 17         /* Indicates the direction of the image when it was captured. */
+#define GPSTAG_MAPDATUM 18             /* Indicates the geodetic survey data used by the GPS receiver. (e.g. WGS-84) */
+#define GPSTAG_DESTLATITUDEREF 19      /* Indicates whether the latitude of the destination point is north or south latitude. */
+#define GPSTAG_DESTLATITUDE 20         /* Indicates the latitude of the destination point. */
+#define GPSTAG_DESTLONGITUDEREF 21     /* Indicates whether the longitude of the destination point is east or west longitude. */
+#define GPSTAG_DESTLONGITUDE 22        /* Indicates the longitude of the destination point. */
+#define GPSTAG_DESTBEARINGREF 23       /* Indicates the reference used for giving the bearing to the destination point. */
+#define GPSTAG_DESTBEARING 24          /* Indicates the bearing to the destination point. */
+#define GPSTAG_DESTDISTANCEREF 25      /* Indicates the unit used to express the distance to the destination point. */
+#define GPSTAG_DESTDISTANCE 26         /* Indicates the distance to the destination point. */
+#define GPSTAG_PROCESSINGMETHOD 27     /* A character string recording the name of the method used for location finding. */
+#define GPSTAG_AREAINFORMATION 28      /* A character string recording the name of the GPS area. */
+#define GPSTAG_DATESTAMP 29            /* A character string recording date and time information relative to UTC (Coordinated Universal Time). */
+#define GPSTAG_DIFFERENTIAL 30         /* Indicates whether differential correction is applied to the GPS receiver. */
+#define GPSTAG_GPSHPOSITIONINGERROR 31 /* Indicates horizontal positioning errors in meters. */
 
 #endif /* _TIFF_ */
diff --git a/third_party/libtiff/tiffio.h b/third_party/libtiff/tiffio.h
index 4df5766..d6bf0cc 100644
--- a/third_party/libtiff/tiffio.h
+++ b/third_party/libtiff/tiffio.h
@@ -263,6 +263,12 @@
     TIFFInitMethod init;
 } TIFFCodec;
 
+typedef struct
+{
+    uint32_t uNum;
+    uint32_t uDenom;
+} TIFFRational_t;
+
 #include <stdarg.h>
 #include <stdio.h>
 
diff --git a/third_party/libtiff/tiffiop.h b/third_party/libtiff/tiffiop.h
index d3ba287..a7cc12b 100644
--- a/third_party/libtiff/tiffiop.h
+++ b/third_party/libtiff/tiffiop.h
@@ -143,15 +143,14 @@
 #define TIFF_CHOPPEDUPARRAYS                                                   \
     0x4000000U /* set when allocChoppedUpStripArrays() has modified strip      \
                   array */
-    uint64_t tif_diroff;      /* file offset of current directory */
-    uint64_t tif_nextdiroff;  /* file offset of following directory */
-    uint64_t tif_lastdiroff;  /* file offset of last directory written so far */
-    uint64_t *tif_dirlistoff; /* list of offsets to already seen directories to
-                                 prevent IFD looping */
+    uint64_t tif_diroff;     /* file offset of current directory */
+    uint64_t tif_nextdiroff; /* file offset of following directory */
+    uint64_t tif_lastdiroff; /* file offset of last directory written so far */
     TIFFHashSet *tif_map_dir_offset_to_number;
     TIFFHashSet *tif_map_dir_number_to_offset;
-    tdir_t tif_dirnumber;  /* number of already seen directories */
-    TIFFDirectory tif_dir; /* internal rep of current directory */
+    int tif_setdirectory_force_absolute; /* switch between relative and absolute
+                                            stepping in TIFFSetDirectory() */
+    TIFFDirectory tif_dir;               /* internal rep of current directory */
     TIFFDirectory
         tif_customdir; /* custom IFDs are separated from the main ones */
     union
@@ -444,6 +443,8 @@
     extern float _TIFFClampDoubleToFloat(double);
     extern uint32_t _TIFFClampDoubleToUInt32(double);
 
+    extern void _TIFFCleanupIFDOffsetAndNumberMaps(TIFF *tif);
+
     extern tmsize_t _TIFFReadEncodedStripAndAllocBuffer(TIFF *tif,
                                                         uint32_t strip,
                                                         void **buf,
diff --git a/third_party/libtiff/tiffvers.h b/third_party/libtiff/tiffvers.h
index 552e885..ed84776 100644
--- a/third_party/libtiff/tiffvers.h
+++ b/third_party/libtiff/tiffvers.h
@@ -3,7 +3,7 @@
 /* clang-format disabled because FindTIFF.cmake is very sensitive to the
  * formatting of below line being a single line.
  */
-#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.5.0\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc."
+#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.5.1\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc."
 /*
  * This define can be used in code that requires
  * compilation-related definitions specific to a
@@ -11,12 +11,12 @@
  * version checking should be done based on the
  * string returned by TIFFGetVersion.
  */
-#define TIFFLIB_VERSION 20221213
+#define TIFFLIB_VERSION 20230609
 
 /* The following defines have been added in 4.5.0 */
 #define TIFFLIB_MAJOR_VERSION 4
 #define TIFFLIB_MINOR_VERSION 5
-#define TIFFLIB_MICRO_VERSION 0
+#define TIFFLIB_MICRO_VERSION 1
 
 /* Macro added in 4.5.0. Returns TRUE if the current libtiff version is
  * greater or equal to major.minor.micro
diff --git a/third_party/ninja/README.pdfium b/third_party/ninja/README.pdfium
index e79bd45..8a5f1ee 100644
--- a/third_party/ninja/README.pdfium
+++ b/third_party/ninja/README.pdfium
@@ -5,6 +5,7 @@
 License: Apache License 2.0
 License File: https://github.com/ninja-build/ninja/blob/master/COPYING
 Security Critical: no
+Shipped: no
 
 Description:
 Ninja is a small build system with a focus on speed, and is used to build
diff --git a/third_party/pymock/README.chromium b/third_party/pymock/README.chromium
index 255c2bf..e49f239 100644
--- a/third_party/pymock/README.chromium
+++ b/third_party/pymock/README.chromium
@@ -2,8 +2,10 @@
 URL: http://pypi.python.org/pypi/mock
 Version: 1.0.1
 Security Critical: no
+Shipped: no
 License: BSD
-License File: NOT_SHIPPED
+License File: LICENSE.txt
+
 Description:
 Python mock library, currently used by native_client_sdk.  This is the
 same mock library that is now part of python 3.3 where it is know as
diff --git a/third_party/skia_shared/SkFloatToDecimal.cpp b/third_party/skia_shared/SkFloatToDecimal.cpp
deleted file mode 100644
index 90d4be3..0000000
--- a/third_party/skia_shared/SkFloatToDecimal.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkFloatToDecimal.h"
-
-#include <cassert>
-#include <cfloat>
-#include <climits>
-#include <cmath>
-
-namespace pdfium {
-namespace skia {
-namespace {
-
-// Return pow(10.0, e), optimized for common cases.
-double pow10(int e) {
-    switch (e) {
-        case 0:  return 1.0;  // common cases
-        case 1:  return 10.0;
-        case 2:  return 100.0;
-        case 3:  return 1e+03;
-        case 4:  return 1e+04;
-        case 5:  return 1e+05;
-        case 6:  return 1e+06;
-        case 7:  return 1e+07;
-        case 8:  return 1e+08;
-        case 9:  return 1e+09;
-        case 10: return 1e+10;
-        case 11: return 1e+11;
-        case 12: return 1e+12;
-        case 13: return 1e+13;
-        case 14: return 1e+14;
-        case 15: return 1e+15;
-        default:
-            if (e > 15) {
-                double value = 1e+15;
-                while (e-- > 15) { value *= 10.0; }
-                return value;
-            } else {
-                assert(e < 0);
-                double value = 1.0;
-                while (e++ < 0) {
-                    value /= 10.0;
-                }
-                return value;
-            }
-    }
-}
-
-}  // namespace
-
-/** Write a string into output, including a terminating '\0' (for
-    unit testing).  Return strlen(output) (for SkWStream::write) The
-    resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
-    sscanf(output, "%f", &x) will return the original value iff the
-    value is finite. This function accepts all possible input values.
-
-    Motivation: "PDF does not support [numbers] in exponential format
-    (such as 6.02e23)."  Otherwise, this function would rely on a
-    sprintf-type function from the standard library. */
-unsigned SkFloatToDecimal(float value, char output[kMaximumSkFloatToDecimalLength]) {
-    /* The longest result is -FLT_MIN.
-       We serialize it as "-.0000000000000000000000000000000000000117549435"
-       which has 48 characters plus a terminating '\0'. */
-
-    static_assert(kMaximumSkFloatToDecimalLength == 49, "");
-    // 3 = '-', '.', and '\0' characters.
-    // 9 = number of significant digits
-    // abs(FLT_MIN_10_EXP) = number of zeros in FLT_MIN
-    static_assert(kMaximumSkFloatToDecimalLength == 3 + 9 - FLT_MIN_10_EXP, "");
-
-    /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
-       most PDF rasterizers will use fixed-point scalars that lack the
-       dynamic range of floats.  Even if this is the case, I want to
-       serialize these (uncommon) very small and very large scalar
-       values with enough precision to allow a floating-point
-       rasterizer to read them in with perfect accuracy.
-       Experimentally, rasterizers such as pdfium do seem to benefit
-       from this.  Rasterizers that rely on fixed-point scalars should
-       gracefully ignore these values that they can not parse. */
-    char* output_ptr = &output[0];
-    const char* const end = &output[kMaximumSkFloatToDecimalLength - 1];
-    // subtract one to leave space for '\0'.
-
-    /* This function is written to accept any possible input value,
-       including non-finite values such as INF and NAN.  In that case,
-       we ignore value-correctness and output a syntacticly-valid
-       number. */
-    if (value == INFINITY) {
-        value = FLT_MAX;  // nearest finite float.
-    }
-    if (value == -INFINITY) {
-        value = -FLT_MAX;  // nearest finite float.
-    }
-    if (!std::isfinite(value) || value == 0.0f) {
-        // NAN is unsupported in PDF.  Always output a valid number.
-        // Also catch zero here, as a special case.
-        *output_ptr++ = '0';
-        *output_ptr = '\0';
-        return static_cast<unsigned>(output_ptr - output);
-    }
-    if (value < 0.0) {
-        *output_ptr++ = '-';
-        value = -value;
-    }
-    assert(value >= 0.0f);
-
-    int binaryExponent;
-    (void)std::frexp(value, &binaryExponent);
-    static const double kLog2 = 0.3010299956639812;  // log10(2.0);
-    int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
-    int decimalShift = decimalExponent - 8;
-    double power = pow10(-decimalShift);
-    assert(value * power <= (double)INT_MAX);
-    int d = static_cast<int>(value * power + 0.5);
-    // assert(value == (float)(d * pow(10.0, decimalShift)));
-    assert(d <= 999999999);
-    if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
-       // need one fewer decimal digits for 24-bit precision.
-       decimalShift = decimalExponent - 7;
-       // assert(power * 0.1 = pow10(-decimalShift));
-       // recalculate to get rounding right.
-       d = static_cast<int>(value * (power * 0.1) + 0.5);
-       assert(d <= 99999999);
-    }
-    while (d % 10 == 0) {
-        d /= 10;
-        ++decimalShift;
-    }
-    assert(d > 0);
-    // assert(value == (float)(d * pow(10.0, decimalShift)));
-    unsigned char buffer[9]; // decimal value buffer.
-    int bufferIndex = 0;
-    do {
-        buffer[bufferIndex++] = d % 10;
-        d /= 10;
-    } while (d != 0);
-    assert(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
-    if (decimalShift >= 0) {
-        do {
-            --bufferIndex;
-            *output_ptr++ = '0' + buffer[bufferIndex];
-        } while (bufferIndex);
-        for (int i = 0; i < decimalShift; ++i) {
-            *output_ptr++ = '0';
-        }
-    } else {
-        int placesBeforeDecimal = bufferIndex + decimalShift;
-        if (placesBeforeDecimal > 0) {
-            while (placesBeforeDecimal-- > 0) {
-                --bufferIndex;
-                *output_ptr++ = '0' + buffer[bufferIndex];
-            }
-            *output_ptr++ = '.';
-        } else {
-            *output_ptr++ = '.';
-            int placesAfterDecimal = -placesBeforeDecimal;
-            while (placesAfterDecimal-- > 0) {
-                *output_ptr++ = '0';
-            }
-        }
-        while (bufferIndex > 0) {
-            --bufferIndex;
-            *output_ptr++ = '0' + buffer[bufferIndex];
-            if (output_ptr == end) {
-                break;  // denormalized: don't need extra precision.
-                // Note: denormalized numbers will not have the same number of
-                // significantDigits, but do not need them to round-trip.
-            }
-        }
-    }
-    assert(output_ptr <= end);
-    *output_ptr = '\0';
-    return static_cast<unsigned>(output_ptr - output);
-}
-}  // namespace skia
-}  // namespace pdfium
diff --git a/third_party/skia_shared/SkFloatToDecimal.h b/third_party/skia_shared/SkFloatToDecimal.h
deleted file mode 100644
index 376b093..0000000
--- a/third_party/skia_shared/SkFloatToDecimal.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkFloatToDecimal_DEFINED
-#define SkFloatToDecimal_DEFINED
-
-namespace pdfium {
-namespace skia {
-
-constexpr unsigned kMaximumSkFloatToDecimalLength = 49;
-
-/** \fn SkFloatToDecimal
-    Convert a float into a decimal string.
-
-    The resulting string will be in the form `[-]?([0-9]*\.)?[0-9]+` (It does
-    not use scientific notation.) and `sscanf(output, "%f", &x)` will return
-    the original value if the value is finite. This function accepts all
-    possible input values.
-
-    INFINITY and -INFINITY are rounded to FLT_MAX and -FLT_MAX.
-
-    NAN values are converted to 0.
-
-    This function will always add a terminating '\0' to the output.
-
-    @param value  Any floating-point number
-    @param output The buffer to write the string into.  Must be non-null.
-
-    @return strlen(output)
-*/
-unsigned SkFloatToDecimal(float value, char output[kMaximumSkFloatToDecimalLength]);
-
-}  // namespace skia
-}  // namespace pdfium
-
-#endif  // SkFloatToDecimal_DEFINED
diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp
index 497beb5..b28cced 100644
--- a/xfa/fde/cfde_texteditengine.cpp
+++ b/xfa/fde/cfde_texteditengine.cpp
@@ -13,7 +13,7 @@
 #include "core/fxcrt/span_util.h"
 #include "core/fxge/text_char_pos.h"
 #include "third_party/base/check.h"
-#include "third_party/base/notreached.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fde/cfde_wordbreak_data.h"
@@ -1130,11 +1130,7 @@
     if (it->nStart <= start_idx && start_idx < it->nStart + it->nCount)
       break;
   }
-  if (it == text_piece_info_.end()) {
-    NOTREACHED();
-    return {0, CFX_RectF()};
-  }
-
+  CHECK_NE(it, text_piece_info_.end());
   return {it->nBidiLevel, GetCharRects(*it)[start_idx - it->nStart]};
 }
 
diff --git a/xfa/fde/cfde_textout.cpp b/xfa/fde/cfde_textout.cpp
index af6ce53..d7582d8 100644
--- a/xfa/fde/cfde_textout.cpp
+++ b/xfa/fde/cfde_textout.cpp
@@ -22,6 +22,7 @@
 #include "core/fxge/fx_font.h"
 #include "core/fxge/text_char_pos.h"
 #include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/layout/cfgas_txtbreak.h"
@@ -129,7 +130,7 @@
 CFDE_TextOut::Piece::~Piece() = default;
 
 CFDE_TextOut::CFDE_TextOut()
-    : m_pTxtBreak(std::make_unique<CFGAS_TxtBreak>()), m_ttoLines(5) {}
+    : m_pTxtBreak(std::make_unique<CFGAS_TxtBreak>()) {}
 
 CFDE_TextOut::~CFDE_TextOut() = default;
 
@@ -344,6 +345,7 @@
     }
     if (m_fLinePos + fLineStep > fLineStop) {
       size_t iCurLine = bEndofLine ? m_iCurLine - 1 : m_iCurLine;
+      CHECK_LT(m_iCurLine, m_ttoLines.size());
       m_ttoLines[iCurLine].set_new_reload(true);
       bRet = true;
       break;
@@ -394,6 +396,7 @@
     }
 
     if (j == chars_to_skip && !bReload) {
+      CHECK_LT(m_iCurLine, m_ttoLines.size());
       m_ttoLines[m_iCurLine].set_new_reload(true);
     } else if (j > chars_to_skip) {
       Piece piece;
diff --git a/xfa/fde/cfde_textout.h b/xfa/fde/cfde_textout.h
index 72ff5b7..af2308b 100644
--- a/xfa/fde/cfde_textout.h
+++ b/xfa/fde/cfde_textout.h
@@ -14,7 +14,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/widestring.h"
 #include "core/fxge/dib/fx_dib.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "xfa/fde/cfde_data.h"
 #include "xfa/fgas/layout/cfgas_break.h"
 #include "xfa/fgas/layout/cfgas_char.h"
diff --git a/xfa/fde/cfde_textout_unittest.cpp b/xfa/fde/cfde_textout_unittest.cpp
index fe078b0..0067302 100644
--- a/xfa/fde/cfde_textout_unittest.cpp
+++ b/xfa/fde/cfde_textout_unittest.cpp
@@ -86,7 +86,14 @@
 
 TEST_F(CFDETextOutTest, DrawLogicTextBasic) {
   text_out().DrawLogicText(device(), L"foo", CFX_RectF(0, 0, 2100, 100));
-  EXPECT_STREQ("b26f1c171fcdbf185823364185adacf0", GetBitmapChecksum().c_str());
+  const char* checksum = []() {
+#if BUILDFLAG(IS_WIN)
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "76fd535f7d490d963598474494d0701e";
+#endif
+    return "b26f1c171fcdbf185823364185adacf0";
+  }();
+  EXPECT_STREQ(checksum, GetBitmapChecksum().c_str());
 }
 
 TEST_F(CFDETextOutTest, DrawLogicTextEmptyRect) {
@@ -117,8 +124,9 @@
   }
 
   const char* GetLargeTextBlobChecksum() {
-    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
-      return "6181929583fd7651169306852397806f";
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+      return "cd357c6afbf17bb2ac48817df5d9eaad";
+    }
     return "268b71a8660b51e31c6bf30fc7ff1e08";
   }
 };
diff --git a/xfa/fgas/crt/cfgas_stringformatter.cpp b/xfa/fgas/crt/cfgas_stringformatter.cpp
index f705175..d073dd7 100644
--- a/xfa/fgas/crt/cfgas_stringformatter.cpp
+++ b/xfa/fgas/crt/cfgas_stringformatter.cpp
@@ -727,8 +727,7 @@
     case CFGAS_StringFormatter::DateTimeType::kTime:
       return CFGAS_StringFormatter::DateTimeType::kTimeDate;
     default:
-      NOTREACHED();
-      return type;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -740,8 +739,7 @@
     case CFGAS_StringFormatter::DateTimeType::kDate:
       return CFGAS_StringFormatter::DateTimeType::kDateTime;
     default:
-      NOTREACHED();
-      return type;
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -1735,7 +1733,6 @@
              ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
                              &iStart);
     case DateTimeType::kUnknown:
-    default:
       return false;
   }
 }
diff --git a/xfa/fgas/crt/cfgas_stringformatter.h b/xfa/fgas/crt/cfgas_stringformatter.h
index 0dfa2bf..0acc76d 100644
--- a/xfa/fgas/crt/cfgas_stringformatter.h
+++ b/xfa/fgas/crt/cfgas_stringformatter.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "core/fxcrt/widestring.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "xfa/fgas/crt/locale_iface.h"
 
 class CFX_DateTime;
diff --git a/xfa/fgas/font/cfgas_fontmgr.cpp b/xfa/fgas/font/cfgas_fontmgr.cpp
index ec0eeea..bb913b4 100644
--- a/xfa/fgas/font/cfgas_fontmgr.cpp
+++ b/xfa/fgas/font/cfgas_fontmgr.cpp
@@ -28,8 +28,8 @@
 #include "core/fxge/fx_font.h"
 #include "third_party/base/check.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/span.h"
 #include "xfa/fgas/font/cfgas_gefont.h"
 #include "xfa/fgas/font/fgas_fontutils.h"
 
diff --git a/xfa/fgas/font/cfgas_fontmgr.h b/xfa/fgas/font/cfgas_fontmgr.h
index 090d638..02cb47a 100644
--- a/xfa/fgas/font/cfgas_fontmgr.h
+++ b/xfa/fgas/font/cfgas_fontmgr.h
@@ -66,9 +66,9 @@
   uint32_t m_dwCsb[2] = {};
 };
 
-class CFGAS_FontDescriptorInfo {
+struct CFGAS_FontDescriptorInfo {
  public:
-  CFGAS_FontDescriptor* pFont;
+  UNOWNED_PTR_EXCLUSION CFGAS_FontDescriptor* pFont;  // POD struct.
   int32_t nPenalty;
 
   bool operator>(const CFGAS_FontDescriptorInfo& other) const {
diff --git a/xfa/fgas/font/fgas_fontutils.cpp b/xfa/fgas/font/fgas_fontutils.cpp
index e8b8876..9cc421c 100644
--- a/xfa/fgas/font/fgas_fontutils.cpp
+++ b/xfa/fgas/font/fgas_fontutils.cpp
@@ -2450,14 +2450,17 @@
 WideString FGAS_FontNameToEnglishName(const WideString& wsLocalName) {
   uint32_t dwLocalNameHash =
       FX_HashCode_GetLoweredW(wsLocalName.AsStringView());
-  const FGAS_FontInfo* pEnd = kXFAFontsMap + std::size(kXFAFontsMap);
+  const FGAS_FontInfo* pBegin = std::begin(kXFAFontsMap);
+  const FGAS_FontInfo* pEnd = std::end(kXFAFontsMap);
   const FGAS_FontInfo* pFontInfo =
-      std::lower_bound(kXFAFontsMap, pEnd, dwLocalNameHash,
+      std::lower_bound(pBegin, pEnd, dwLocalNameHash,
                        [](const FGAS_FontInfo& entry, uint32_t hash) {
                          return entry.dwFontNameHash < hash;
                        });
-  if (pFontInfo < pEnd && pFontInfo->dwFontNameHash == dwLocalNameHash)
+
+  if (pFontInfo < pEnd && pFontInfo->dwFontNameHash == dwLocalNameHash) {
     return WideString::FromASCII(ByteStringView(pFontInfo->pPsName));
+  }
   return wsLocalName;
 }
 
@@ -2466,13 +2469,16 @@
   wsFontNameTemp.Remove(L' ');
   uint32_t dwCurFontNameHash =
       FX_HashCode_GetLoweredW(wsFontNameTemp.AsStringView());
-  const FGAS_FontInfo* pEnd = kXFAFontsMap + std::size(kXFAFontsMap);
+  const FGAS_FontInfo* pBegin = std::begin(kXFAFontsMap);
+  const FGAS_FontInfo* pEnd = std::end(kXFAFontsMap);
   const FGAS_FontInfo* pFontInfo =
-      std::lower_bound(kXFAFontsMap, pEnd, dwCurFontNameHash,
+      std::lower_bound(pBegin, pEnd, dwCurFontNameHash,
                        [](const FGAS_FontInfo& entry, uint32_t hash) {
                          return entry.dwFontNameHash < hash;
                        });
-  if (pFontInfo < pEnd && pFontInfo->dwFontNameHash == dwCurFontNameHash)
+
+  if (pFontInfo < pEnd && pFontInfo->dwFontNameHash == dwCurFontNameHash) {
     return pFontInfo;
+  }
   return nullptr;
 }
diff --git a/xfa/fgas/graphics/cfgas_gegraphics.cpp b/xfa/fgas/graphics/cfgas_gegraphics.cpp
index c909183..cb2b959 100644
--- a/xfa/fgas/graphics/cfgas_gegraphics.cpp
+++ b/xfa/fgas/graphics/cfgas_gegraphics.cpp
@@ -19,7 +19,6 @@
 #include "core/fxge/cfx_unicodeencoding.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "third_party/base/check.h"
-#include "third_party/base/notreached.h"
 #include "xfa/fgas/graphics/cfgas_gecolor.h"
 #include "xfa/fgas/graphics/cfgas_gepath.h"
 #include "xfa/fgas/graphics/cfgas_gepattern.h"
@@ -129,10 +128,7 @@
 
 void CFGAS_GEGraphics::RestoreGraphState() {
   m_renderDevice->RestoreState(false);
-  if (m_infoStack.empty()) {
-    NOTREACHED();
-    return;
-  }
+  CHECK(!m_infoStack.empty());
   m_info = *m_infoStack.back();
   m_infoStack.pop_back();
   return;
@@ -261,7 +257,7 @@
   auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
   mask->Create(data.width, data.height, FXDIB_Format::k1bppMask);
   fxcrt::spancpy(
-      mask->GetBuffer(),
+      mask->GetWritableBuffer(),
       pdfium::make_span(data.maskBits).first(mask->GetPitch() * data.height));
   const CFX_FloatRect rectf =
       matrix.TransformRect(path.GetPath().GetBoundingBox());
@@ -390,10 +386,6 @@
       result = true;
       break;
     }
-    default: {
-      result = false;
-      break;
-    }
   }
   if (result) {
     CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
diff --git a/xfa/fgas/graphics/cfgas_gegraphics.h b/xfa/fgas/graphics/cfgas_gegraphics.h
index cf86c38..8e4e408 100644
--- a/xfa/fgas/graphics/cfgas_gegraphics.h
+++ b/xfa/fgas/graphics/cfgas_gegraphics.h
@@ -16,7 +16,7 @@
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "xfa/fgas/graphics/cfgas_gecolor.h"
 
 class CFGAS_GEPath;
diff --git a/xfa/fgas/layout/cfgas_rtfbreak.cpp b/xfa/fgas/layout/cfgas_rtfbreak.cpp
index 66192dc..540a226 100644
--- a/xfa/fgas/layout/cfgas_rtfbreak.cpp
+++ b/xfa/fgas/layout/cfgas_rtfbreak.cpp
@@ -113,7 +113,6 @@
     case FX_CHARTYPE::kSpace:
     case FX_CHARTYPE::kNumeric:
     case FX_CHARTYPE::kNormal:
-    default:
       dwRet2 = AppendChar_Others(pCurChar);
       break;
   }
diff --git a/xfa/fgas/layout/cfgas_txtbreak.cpp b/xfa/fgas/layout/cfgas_txtbreak.cpp
index e00ab02..22a6277 100644
--- a/xfa/fgas/layout/cfgas_txtbreak.cpp
+++ b/xfa/fgas/layout/cfgas_txtbreak.cpp
@@ -260,7 +260,6 @@
       case FX_CHARTYPE::kSpace:
       case FX_CHARTYPE::kNumeric:
       case FX_CHARTYPE::kNormal:
-      default:
         dwRet2 = AppendChar_Others(pCurChar);
         break;
     }
diff --git a/xfa/fgas/layout/cfgas_txtbreak.h b/xfa/fgas/layout/cfgas_txtbreak.h
index ae69a24..c7b9931 100644
--- a/xfa/fgas/layout/cfgas_txtbreak.h
+++ b/xfa/fgas/layout/cfgas_txtbreak.h
@@ -12,6 +12,8 @@
 
 #include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "xfa/fgas/layout/cfgas_break.h"
 #include "xfa/fgas/layout/cfgas_char.h"
 
@@ -49,9 +51,9 @@
     Run(const Run& other);
     ~Run();
 
-    CFGAS_TxtBreak::Engine* pEdtEngine = nullptr;
+    UnownedPtr<CFGAS_TxtBreak::Engine> pEdtEngine;
     WideString wsStr;
-    int32_t* pWidths = nullptr;
+    UNOWNED_PTR_EXCLUSION int32_t* pWidths = nullptr;
     // TODO(thestig): These 2 members probably should be size_t.
     int32_t iStart = 0;
     int32_t iLength = 0;
@@ -61,7 +63,7 @@
     int32_t iHorizontalScale = 100;
     int32_t iVerticalScale = 100;
     uint32_t dwCharStyles = 0;
-    const CFX_RectF* pRect = nullptr;
+    UnownedPtr<const CFX_RectF> pRect;
     bool bSkipSpace = true;
   };
 
diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp
index f95176d..9d624c9 100644
--- a/xfa/fwl/cfwl_edit.cpp
+++ b/xfa/fwl/cfwl_edit.cpp
@@ -15,7 +15,6 @@
 #include "core/fxge/cfx_renderdevice.h"
 #include "core/fxge/text_char_pos.h"
 #include "third_party/base/check.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "v8/include/cppgc/visitor.h"
 #include "xfa/fde/cfde_textout.h"
@@ -583,7 +582,7 @@
     float fRange =
         std::max(contents_bounds.height - m_EngineRect.height, fStep);
     m_pVertScrollBar->SetRange(0.0f, fRange);
-    float fPos = pdfium::clamp(m_fScrollOffsetY, 0.0f, fRange);
+    float fPos = std::clamp(m_fScrollOffsetY, 0.0f, fRange);
     m_pVertScrollBar->SetPos(fPos);
     m_pVertScrollBar->SetTrackPos(fPos);
     m_pVertScrollBar->SetPageSize(rtScroll.height);
diff --git a/xfa/fwl/cfwl_event.h b/xfa/fwl/cfwl_event.h
index 385abec..ab9606d 100644
--- a/xfa/fwl/cfwl_event.h
+++ b/xfa/fwl/cfwl_event.h
@@ -7,6 +7,7 @@
 #ifndef XFA_FWL_CFWL_EVENT_H_
 #define XFA_FWL_CFWL_EVENT_H_
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "v8/include/cppgc/macros.h"
 
 class CFWL_Widget;
@@ -41,8 +42,8 @@
 
  private:
   const Type m_type;
-  CFWL_Widget* const m_pSrcTarget = nullptr;
-  CFWL_Widget* const m_pDstTarget = nullptr;
+  UnownedPtr<CFWL_Widget> const m_pSrcTarget;
+  UnownedPtr<CFWL_Widget> const m_pDstTarget;
 };
 
 #endif  // XFA_FWL_CFWL_EVENT_H_
diff --git a/xfa/fwl/cfwl_listbox.cpp b/xfa/fwl/cfwl_listbox.cpp
index dbd29e1..9f4c89a 100644
--- a/xfa/fwl/cfwl_listbox.cpp
+++ b/xfa/fwl/cfwl_listbox.cpp
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include "core/fxcrt/stl_util.h"
-#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "v8/include/cppgc/visitor.h"
 #include "xfa/fde/cfde_textout.h"
@@ -463,7 +462,7 @@
     m_pVertScrollBar->SetPageSize(rtScrollBar.height * 9 / 10);
     m_pVertScrollBar->SetStepSize(m_fItemHeight);
 
-    float fPos = pdfium::clamp(m_pVertScrollBar->GetPos(), 0.0f, fMax);
+    float fPos = std::clamp(m_pVertScrollBar->GetPos(), 0.0f, fMax);
     m_pVertScrollBar->SetPos(fPos);
     m_pVertScrollBar->SetTrackPos(fPos);
     if ((m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
@@ -493,7 +492,7 @@
     m_pHorzScrollBar->SetPageSize(fWidth * 9 / 10);
     m_pHorzScrollBar->SetStepSize(fWidth / 10);
 
-    float fPos = pdfium::clamp(m_pHorzScrollBar->GetPos(), 0.0f, fMax);
+    float fPos = std::clamp(m_pHorzScrollBar->GetPos(), 0.0f, fMax);
     m_pHorzScrollBar->SetPos(fPos);
     m_pHorzScrollBar->SetTrackPos(fPos);
     if ((m_Properties.m_dwStyleExts & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
@@ -617,8 +616,6 @@
         OnKeyDown(pMsg);
       break;
     }
-    default:
-      break;
   }
   // Dst target could be |this|, continue only if not destroyed by above.
   if (pMessage->GetDstTarget())
diff --git a/xfa/fwl/cfwl_listbox.h b/xfa/fwl/cfwl_listbox.h
index 9f27b65..2236842 100644
--- a/xfa/fwl/cfwl_listbox.h
+++ b/xfa/fwl/cfwl_listbox.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include <vector>
 
+#include "core/fxcrt/unowned_ptr.h"
 #include "xfa/fwl/cfwl_edit.h"
 #include "xfa/fwl/cfwl_event.h"
 #include "xfa/fwl/cfwl_listbox.h"
@@ -136,8 +137,8 @@
   bool m_bLButtonDown = false;
   float m_fItemHeight = 0.0f;
   float m_fScorllBarWidth = 0.0f;
-  Item* m_hAnchor = nullptr;
-  std::vector<std::unique_ptr<Item>> m_ItemArray;
+  std::vector<std::unique_ptr<Item>> m_ItemArray;  // Must outlive `m_hAnchor`.
+  UnownedPtr<Item> m_hAnchor;
 };
 
 #endif  // XFA_FWL_CFWL_LISTBOX_H_
diff --git a/xfa/fwl/cfwl_monthcalendar.cpp b/xfa/fwl/cfwl_monthcalendar.cpp
index 145198b..5867ec9 100644
--- a/xfa/fwl/cfwl_monthcalendar.cpp
+++ b/xfa/fwl/cfwl_monthcalendar.cpp
@@ -50,8 +50,7 @@
     case 6:
       return L"Sat";
     default:
-      NOTREACHED();
-      return L"";
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -82,8 +81,7 @@
     case 11:
       return L"December";
     default:
-      NOTREACHED();
-      return L"";
+      NOTREACHED_NORETURN();
   }
 }
 
diff --git a/xfa/fwl/cfwl_notedriver.cpp b/xfa/fwl/cfwl_notedriver.cpp
index e6bcfe8..f3506d6 100644
--- a/xfa/fwl/cfwl_notedriver.cpp
+++ b/xfa/fwl/cfwl_notedriver.cpp
@@ -131,8 +131,6 @@
         return false;
       break;
     }
-    default:
-      break;
   }
   IFWL_WidgetDelegate* pDelegate = pMessage->GetDstTarget()->GetDelegate();
   if (pDelegate)
diff --git a/xfa/fwl/cfwl_scrollbar.cpp b/xfa/fwl/cfwl_scrollbar.cpp
index 36dd9e5..4bf4b36 100644
--- a/xfa/fwl/cfwl_scrollbar.cpp
+++ b/xfa/fwl/cfwl_scrollbar.cpp
@@ -10,7 +10,6 @@
 #include <memory>
 #include <utility>
 
-#include "third_party/base/cxx17_backports.h"
 #include "xfa/fwl/cfwl_app.h"
 #include "xfa/fwl/cfwl_messagemouse.h"
 #include "xfa/fwl/cfwl_messagemousewheel.h"
@@ -200,7 +199,7 @@
   fThumbSize = std::max(fThumbSize, kMinThumbSize);
 
   float fDiff = std::max(fLength - fThumbSize, 0.0f);
-  float fTrackPos = pdfium::clamp(m_fTrackPos, m_fRangeMin, m_fRangeMax);
+  float fTrackPos = std::clamp(m_fTrackPos, m_fRangeMin, m_fRangeMax);
   if (!fRange)
     return rect;
 
@@ -268,7 +267,7 @@
   }
 
   fPos += m_fLastTrackPos;
-  return pdfium::clamp(fPos, m_fRangeMin, m_fRangeMax);
+  return std::clamp(fPos, m_fRangeMin, m_fRangeMax);
 }
 
 bool CFWL_ScrollBar::SendEvent() {
diff --git a/xfa/fxfa/cxfa_ffline.cpp b/xfa/fxfa/cxfa_ffline.cpp
index b8f7815..47ebc2e 100644
--- a/xfa/fxfa/cxfa_ffline.cpp
+++ b/xfa/fxfa/cxfa_ffline.cpp
@@ -48,8 +48,7 @@
       case XFA_AttributeValue::Even:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   } else if (rect.width < 1.0f) {
     switch (iHand) {
@@ -62,8 +61,7 @@
       case XFA_AttributeValue::Even:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   } else {
     switch (iHand) {
@@ -76,8 +74,7 @@
       case XFA_AttributeValue::Even:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   }
 }
diff --git a/xfa/fxfa/cxfa_textlayout.cpp b/xfa/fxfa/cxfa_textlayout.cpp
index 10782c0..8228a5c 100644
--- a/xfa/fxfa/cxfa_textlayout.cpp
+++ b/xfa/fxfa/cxfa_textlayout.cpp
@@ -182,8 +182,7 @@
       case XFA_AttributeValue::Radix:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
     m_pBreak->SetAlignment(iAlign);
 
@@ -715,8 +714,7 @@
         break;
       }
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   }
 
diff --git a/xfa/fxfa/cxfa_textparser.cpp b/xfa/fxfa/cxfa_textparser.cpp
index c503689..15b5178 100644
--- a/xfa/fxfa/cxfa_textparser.cpp
+++ b/xfa/fxfa/cxfa_textparser.cpp
@@ -135,8 +135,7 @@
       case XFA_AttributeValue::Radix:
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
     pStyle->SetTextAlign(hAlign);
     CFX_CSSRect rtMarginWidth;
diff --git a/xfa/fxfa/formcalc/cxfa_fmparser.h b/xfa/fxfa/formcalc/cxfa_fmparser.h
index 1f02f60..e8b6c44 100644
--- a/xfa/fxfa/formcalc/cxfa_fmparser.h
+++ b/xfa/fxfa/formcalc/cxfa_fmparser.h
@@ -9,7 +9,7 @@
 
 #include <vector>
 
-#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "fxjs/gc/heap.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "v8/include/cppgc/macros.h"
@@ -64,7 +64,7 @@
   ParseArgumentList();
 
   UnownedPtr<cppgc::Heap> const m_heap;
-  UnownedPtr<CXFA_FMLexer> m_lexer;
+  UNOWNED_PTR_EXCLUSION CXFA_FMLexer* const m_lexer;  // Stack allocated.
   CXFA_FMLexer::Token m_token;
   bool m_error = false;
   unsigned long m_parse_depth = 0;
diff --git a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
index 1d827f3..96eb05f 100644
--- a/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
+++ b/xfa/fxfa/layout/cxfa_contentlayoutprocessor.cpp
@@ -78,7 +78,7 @@
       break;
     }
     default:
-      NOTREACHED();
+      NOTREACHED_NORETURN();
   }
 }
 
@@ -1849,7 +1849,6 @@
             case Result::kRowFullBreak:
               goto SuspendAndCreateNewRow;
             case Result::kDone:
-            default:
               fContentCurRowY +=
                   pProcessor->InsertPendingItems(m_pCurChildNode);
               pProcessor = nullptr;
@@ -1859,8 +1858,6 @@
         }
         case Stage::kDone:
           break;
-        default:
-          break;
       }
       GotoNextContainerNodeSimple();
       if (bAddedItemInRow && eFlowStrategy == XFA_AttributeValue::Tb)
diff --git a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
index e48c9df..859621d 100644
--- a/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
+++ b/xfa/fxfa/layout/cxfa_viewlayoutprocessor.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "fxjs/gc/heap.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "v8/include/cppgc/garbage-collected.h"
@@ -33,16 +34,16 @@
   struct BreakData {
     CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
    public:
-    CXFA_Node* pLeader;
-    CXFA_Node* pTrailer;
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pLeader;   // POD struct.
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pTrailer;  // POD struct.
     bool bCreatePage;
   };
 
   struct OverflowData {
     CPPGC_STACK_ALLOCATED();  // Raw/Unowned pointers allowed.
    public:
-    CXFA_Node* pLeader;
-    CXFA_Node* pTrailer;
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pLeader;   // POD struct.
+    UNOWNED_PTR_EXCLUSION CXFA_Node* pTrailer;  // POD struct.
   };
 
   CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
diff --git a/xfa/fxfa/parser/cxfa_box.cpp b/xfa/fxfa/parser/cxfa_box.cpp
index 761c7c8..cd644aa 100644
--- a/xfa/fxfa/parser/cxfa_box.cpp
+++ b/xfa/fxfa/parser/cxfa_box.cpp
@@ -207,7 +207,7 @@
   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
     ToRectangle(this)->Draw(strokes, pGS, rtWidget, matrix);
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
 }
 
@@ -237,7 +237,7 @@
   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
     ToRectangle(this)->GetFillPath(strokes, rtWidget, &fillPath);
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
   fillPath.Close();
   fill->Draw(pGS, fillPath, rtWidget, matrix);
diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp
index 70464c4..45c7849 100644
--- a/xfa/fxfa/parser/cxfa_document.cpp
+++ b/xfa/fxfa/parser/cxfa_document.cpp
@@ -1648,8 +1648,7 @@
     case XFA_Element::Variables:
       return nullptr;
     default:
-      NOTREACHED();
-      return nullptr;
+      NOTREACHED_NORETURN();
   }
 }
 
diff --git a/xfa/fxfa/parser/cxfa_document.h b/xfa/fxfa/parser/cxfa_document.h
index 585ee4c..3b33483 100644
--- a/xfa/fxfa/parser/cxfa_document.h
+++ b/xfa/fxfa/parser/cxfa_document.h
@@ -18,7 +18,7 @@
 #include "core/fxcrt/widestring.h"
 #include "fxjs/gc/heap.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "v8/include/cppgc/garbage-collected.h"
 #include "v8/include/cppgc/member.h"
 #include "v8/include/cppgc/persistent.h"
diff --git a/xfa/fxfa/parser/cxfa_document_builder.cpp b/xfa/fxfa/parser/cxfa_document_builder.cpp
index 6fc52b7..c103284 100644
--- a/xfa/fxfa/parser/cxfa_document_builder.cpp
+++ b/xfa/fxfa/parser/cxfa_document_builder.cpp
@@ -170,8 +170,7 @@
         break;
       }
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
   }
 }
diff --git a/xfa/fxfa/parser/cxfa_localemgr.cpp b/xfa/fxfa/parser/cxfa_localemgr.cpp
index 53444b0..f49275e 100644
--- a/xfa/fxfa/parser/cxfa_localemgr.cpp
+++ b/xfa/fxfa/parser/cxfa_localemgr.cpp
@@ -1199,7 +1199,6 @@
     case LangID::k_ru_RU:
       return GetLocaleFromBuffer(m_pHeap, k_ruRU_Locale);
     case LangID::k_en_US:
-    default:
       return GetLocaleFromBuffer(m_pHeap, k_enUS_Locale);
   }
 }
diff --git a/xfa/fxfa/parser/cxfa_localevalue.cpp b/xfa/fxfa/parser/cxfa_localevalue.cpp
index 165fe26..b3b0970 100644
--- a/xfa/fxfa/parser/cxfa_localevalue.cpp
+++ b/xfa/fxfa/parser/cxfa_localevalue.cpp
@@ -15,7 +15,7 @@
 #include "core/fxcrt/cfx_datetime.h"
 #include "core/fxcrt/fx_extension.h"
 #include "third_party/base/check.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "xfa/fgas/crt/cfgas_stringformatter.h"
 #include "xfa/fxfa/parser/cxfa_document.h"
 #include "xfa/fxfa/parser/cxfa_localemgr.h"
diff --git a/xfa/fxfa/parser/cxfa_measurement.cpp b/xfa/fxfa/parser/cxfa_measurement.cpp
index db71075..94675c8 100644
--- a/xfa/fxfa/parser/cxfa_measurement.cpp
+++ b/xfa/fxfa/parser/cxfa_measurement.cpp
@@ -127,8 +127,7 @@
       *fValue /= kPtToPc;
       return true;
     default:
-      NOTREACHED();
-      return false;
+      NOTREACHED_NORETURN();
   }
 }
 
diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp
index e508136..2bf0f16 100644
--- a/xfa/fxfa/parser/cxfa_node.cpp
+++ b/xfa/fxfa/parser/cxfa_node.cpp
@@ -36,8 +36,8 @@
 #include "third_party/base/check.h"
 #include "third_party/base/check_op.h"
 #include "third_party/base/containers/contains.h"
+#include "third_party/base/containers/span.h"
 #include "third_party/base/notreached.h"
-#include "third_party/base/span.h"
 #include "xfa/fde/cfde_textout.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fgas/crt/locale_iface.h"
@@ -2918,7 +2918,7 @@
       widget_type = XFA_FFWidgetType::kTextEdit;
     }
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
 
   if (!pUIChild) {
@@ -2933,8 +2933,7 @@
 }
 
 XFA_FFWidgetType CXFA_Node::GetDefaultFFWidgetType() const {
-  NOTREACHED();
-  return XFA_FFWidgetType::kNone;
+  NOTREACHED_NORETURN();
 }
 
 CXFA_Node* CXFA_Node::CreateUINodeIfNeeded(CXFA_Ui* ui, XFA_Element type) {
@@ -2979,7 +2978,7 @@
   } else if (type == XFA_Element::ExclGroup) {
     ff_widget_type_ = XFA_FFWidgetType::kExclGroup;
   } else {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   }
   return ui_ ? ui_->GetFirstChild() : nullptr;
 }
@@ -3710,8 +3709,7 @@
           fStartOffset += (fHeight - fTextHeight + fSpaceAbove);
           break;
         default:
-          NOTREACHED();
-          break;
+          NOTREACHED_NORETURN();
       }
     }
     if (fStartOffset < 0.1f)
@@ -5096,7 +5094,7 @@
       ToXMLText(GetXMLMappingNode())->SetText(value);
       break;
     default:
-      NOTREACHED();
+      NOTREACHED_NORETURN();
   }
 }
 
diff --git a/xfa/fxfa/parser/cxfa_node.h b/xfa/fxfa/parser/cxfa_node.h
index 9bffa7d..08e19f6 100644
--- a/xfa/fxfa/parser/cxfa_node.h
+++ b/xfa/fxfa/parser/cxfa_node.h
@@ -17,11 +17,12 @@
 #include "core/fxcrt/mask.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "core/fxcrt/unowned_ptr_exclusion.h"
 #include "core/fxcrt/widestring.h"
 #include "core/fxge/dib/fx_dib.h"
 #include "fxjs/gc/gced_tree_node_mixin.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "v8/include/cppgc/member.h"
 #include "v8/include/cppgc/visitor.h"
 #include "xfa/fxfa/cxfa_ffwidget_type.h"
@@ -108,7 +109,7 @@
   struct AttributeData {
     XFA_Attribute attribute;
     XFA_AttributeType type;
-    void* default_value;
+    UNOWNED_PTR_EXCLUSION void* default_value;  // POD type.
   };
 
   // Node is created from cppgc heap.
diff --git a/xfa/fxfa/parser/cxfa_node_unittest.cpp b/xfa/fxfa/parser/cxfa_node_unittest.cpp
index 2c745ca..e00772c 100644
--- a/xfa/fxfa/parser/cxfa_node_unittest.cpp
+++ b/xfa/fxfa/parser/cxfa_node_unittest.cpp
@@ -411,3 +411,14 @@
   EXPECT_TRUE(GetNode()->IsAncestorOf(grandchild));
   EXPECT_FALSE(child1->IsAncestorOf(grandchild));
 }
+
+TEST_F(CXFANodeTest, DeltaObjectIsNode) {
+  CXFA_Node* delta =
+      CXFA_Node::Create(GetDoc(), XFA_Element::Delta, XFA_PacketType::Form);
+  ASSERT_TRUE(delta);
+  ASSERT_TRUE(delta->IsNode());
+
+  // This call should not crash, like in crbug.com/1465239.
+  delta->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, L"delta",
+                                        /*bNotify=*/false);
+}
diff --git a/xfa/fxfa/parser/cxfa_object.h b/xfa/fxfa/parser/cxfa_object.h
index 84c4e98..c482d3b 100644
--- a/xfa/fxfa/parser/cxfa_object.h
+++ b/xfa/fxfa/parser/cxfa_object.h
@@ -59,7 +59,8 @@
            m_objectType == XFA_ObjectType::ModelNode ||
            m_objectType == XFA_ObjectType::TextNode ||
            m_objectType == XFA_ObjectType::ContainerNode ||
-           m_objectType == XFA_ObjectType::ContentNode;
+           m_objectType == XFA_ObjectType::ContentNode ||
+           m_elementType == XFA_Element::Delta;
   }
   bool IsTreeList() const { return m_objectType == XFA_ObjectType::TreeList; }
   bool IsContentNode() const {
diff --git a/xfa/fxfa/parser/cxfa_rectangle.cpp b/xfa/fxfa/parser/cxfa_rectangle.cpp
index 60ac294..c6be5ac 100644
--- a/xfa/fxfa/parser/cxfa_rectangle.cpp
+++ b/xfa/fxfa/parser/cxfa_rectangle.cpp
@@ -303,8 +303,7 @@
         StrokeEmbossed(pGS, rtWidget, fThickness, matrix);
         break;
       default:
-        NOTREACHED();
-        break;
+        NOTREACHED_NORETURN();
     }
     return;
   }
diff --git a/xfa/fxfa/parser/cxfa_xmllocale.h b/xfa/fxfa/parser/cxfa_xmllocale.h
index 726a4fa..0c045ff 100644
--- a/xfa/fxfa/parser/cxfa_xmllocale.h
+++ b/xfa/fxfa/parser/cxfa_xmllocale.h
@@ -11,7 +11,7 @@
 
 #include "core/fxcrt/unowned_ptr.h"
 #include "fxjs/gc/heap.h"
-#include "third_party/base/span.h"
+#include "third_party/base/containers/span.h"
 #include "v8/include/cppgc/garbage-collected.h"
 #include "xfa/fxfa/parser/gced_locale_iface.h"